Browse Source

Add Nullability to spring-security-crypto

Closes gh-17533
pull/17587/head
Rob Winch 7 months ago
parent
commit
9db1ffbd79
No known key found for this signature in database
  1. 4
      crypto/spring-security-crypto.gradle
  2. 20
      crypto/src/main/java/org/springframework/security/crypto/argon2/package-info.java
  3. 4
      crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java
  4. 9
      crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java
  5. 20
      crypto/src/main/java/org/springframework/security/crypto/bcrypt/package-info.java
  6. 4
      crypto/src/main/java/org/springframework/security/crypto/codec/Utf8.java
  7. 3
      crypto/src/main/java/org/springframework/security/crypto/codec/package-info.java
  8. 9
      crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java
  9. 5
      crypto/src/main/java/org/springframework/security/crypto/encrypt/CipherUtils.java
  10. 4
      crypto/src/main/java/org/springframework/security/crypto/encrypt/KeyStoreKeyFactory.java
  11. 3
      crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java
  12. 16
      crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaRawEncryptor.java
  13. 15
      crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java
  14. 20
      crypto/src/main/java/org/springframework/security/crypto/encrypt/package-info.java
  15. 20
      crypto/src/main/java/org/springframework/security/crypto/factory/package-info.java
  16. 20
      crypto/src/main/java/org/springframework/security/crypto/keygen/package-info.java
  17. 16
      crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java
  18. 16
      crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java
  19. 6
      crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoderUtils.java
  20. 20
      crypto/src/main/java/org/springframework/security/crypto/password/package-info.java
  21. 20
      crypto/src/main/java/org/springframework/security/crypto/scrypt/package-info.java
  22. 20
      crypto/src/main/java/org/springframework/security/crypto/util/package-info.java

4
crypto/spring-security-crypto.gradle

@ -1,3 +1,7 @@ @@ -1,3 +1,7 @@
plugins {
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module'
dependencies {

20
crypto/src/main/java/org/springframework/security/crypto/argon2/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.argon2;
import org.jspecify.annotations.NullMarked;

4
crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java

@ -210,9 +210,9 @@ public class BCrypt { @@ -210,9 +210,9 @@ public class BCrypt {
static final int MAX_LOG_ROUNDS = 31;
// Expanded Blowfish key
private int P[];
private int P[] = new int[0];
private int S[];
private int S[] = new int[0];
/**
* Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note

9
crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java

@ -22,6 +22,7 @@ import java.util.regex.Pattern; @@ -22,6 +22,7 @@ import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.password.PasswordEncoder;
@ -43,7 +44,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder { @@ -43,7 +44,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
private final BCryptVersion version;
private final SecureRandom random;
private final @Nullable SecureRandom random;
public BCryptPasswordEncoder() {
this(-1);
@ -67,7 +68,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder { @@ -67,7 +68,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
* @param version the version of bcrypt, can be 2a,2b,2y
* @param random the secure random instance to use
*/
public BCryptPasswordEncoder(BCryptVersion version, SecureRandom random) {
public BCryptPasswordEncoder(BCryptVersion version, @Nullable SecureRandom random) {
this(version, -1, random);
}
@ -75,7 +76,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder { @@ -75,7 +76,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
* @param strength the log rounds to use, between 4 and 31
* @param random the secure random instance to use
*/
public BCryptPasswordEncoder(int strength, SecureRandom random) {
public BCryptPasswordEncoder(int strength, @Nullable SecureRandom random) {
this(BCryptVersion.$2A, strength, random);
}
@ -92,7 +93,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder { @@ -92,7 +93,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
* @param strength the log rounds to use, between 4 and 31
* @param random the secure random instance to use
*/
public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
public BCryptPasswordEncoder(BCryptVersion version, int strength, @Nullable SecureRandom random) {
if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
throw new IllegalArgumentException("Bad strength");
}

20
crypto/src/main/java/org/springframework/security/crypto/bcrypt/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.bcrypt;
import org.jspecify.annotations.NullMarked;

4
crypto/src/main/java/org/springframework/security/crypto/codec/Utf8.java

@ -22,6 +22,8 @@ import java.nio.charset.CharacterCodingException; @@ -22,6 +22,8 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.jspecify.annotations.Nullable;
/**
* UTF-8 Charset encoder/decoder.
* <p>
@ -39,7 +41,7 @@ public final class Utf8 { @@ -39,7 +41,7 @@ public final class Utf8 {
/**
* Get the bytes of the String in UTF-8 encoded form.
*/
public static byte[] encode(CharSequence string) {
public static byte[] encode(@Nullable CharSequence string) {
try {
ByteBuffer bytes = CHARSET.newEncoder().encode(CharBuffer.wrap(string));
byte[] bytesCopy = new byte[bytes.limit()];

3
crypto/src/main/java/org/springframework/security/crypto/codec/package-info.java

@ -17,4 +17,7 @@ @@ -17,4 +17,7 @@
/**
* Internal codec classes. Only intended for use within the framework.
*/
@NullMarked
package org.springframework.security.crypto.codec;
import org.jspecify.annotations.NullMarked;

9
crypto/src/main/java/org/springframework/security/crypto/encrypt/AesBytesEncryptor.java

@ -25,6 +25,8 @@ import javax.crypto.spec.IvParameterSpec; @@ -25,6 +25,8 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
@ -81,7 +83,7 @@ public final class AesBytesEncryptor implements BytesEncryptor { @@ -81,7 +83,7 @@ public final class AesBytesEncryptor implements BytesEncryptor {
* @param salt the hex-encoded salt value
* @param ivGenerator the generator used to generate the initialization vector
*/
public AesBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator) {
public AesBytesEncryptor(String password, CharSequence salt, @Nullable BytesKeyGenerator ivGenerator) {
this(password, salt, ivGenerator, CipherAlgorithm.CBC);
}
@ -95,7 +97,8 @@ public final class AesBytesEncryptor implements BytesEncryptor { @@ -95,7 +97,8 @@ public final class AesBytesEncryptor implements BytesEncryptor {
* @param ivGenerator the generator used to generate the initialization vector
* @param alg the {@link CipherAlgorithm} to be used
*/
public AesBytesEncryptor(String password, CharSequence salt, BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
public AesBytesEncryptor(String password, CharSequence salt, @Nullable BytesKeyGenerator ivGenerator,
CipherAlgorithm alg) {
this(CipherUtils.newSecretKey("PBKDF2WithHmacSHA1",
new PBEKeySpec(password.toCharArray(), Hex.decode(salt), 1024, 256)), ivGenerator, alg);
}
@ -108,7 +111,7 @@ public final class AesBytesEncryptor implements BytesEncryptor { @@ -108,7 +111,7 @@ public final class AesBytesEncryptor implements BytesEncryptor {
* {@link CipherAlgorithm}
* @param alg the {@link CipherAlgorithm} to be used
*/
public AesBytesEncryptor(SecretKey secretKey, BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
public AesBytesEncryptor(SecretKey secretKey, @Nullable BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
this.alg = alg;
this.encryptor = alg.createCipher();

5
crypto/src/main/java/org/springframework/security/crypto/encrypt/CipherUtils.java

@ -32,6 +32,8 @@ import javax.crypto.SecretKeyFactory; @@ -32,6 +32,8 @@ import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.jspecify.annotations.Nullable;
/**
* Static helper for working with the Cipher API.
*
@ -109,7 +111,8 @@ final class CipherUtils { @@ -109,7 +111,8 @@ final class CipherUtils {
/**
* Initializes the Cipher for use.
*/
static void initCipher(Cipher cipher, int mode, SecretKey secretKey, AlgorithmParameterSpec parameterSpec) {
static void initCipher(Cipher cipher, int mode, SecretKey secretKey,
@Nullable AlgorithmParameterSpec parameterSpec) {
try {
if (parameterSpec != null) {
cipher.init(mode, secretKey, parameterSpec);

4
crypto/src/main/java/org/springframework/security/crypto/encrypt/KeyStoreKeyFactory.java

@ -25,6 +25,8 @@ import java.security.cert.Certificate; @@ -25,6 +25,8 @@ import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.RSAPublicKeySpec;
import org.jspecify.annotations.Nullable;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
@ -39,7 +41,7 @@ public class KeyStoreKeyFactory { @@ -39,7 +41,7 @@ public class KeyStoreKeyFactory {
private final char[] password;
private KeyStore store;
private @Nullable KeyStore store;
private final Object lock = new Object();

3
crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java

@ -44,6 +44,7 @@ import java.util.regex.Matcher; @@ -44,6 +44,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bouncycastle.asn1.ASN1Sequence;
import org.jspecify.annotations.Nullable;
/**
* Reads RSA key pairs using BC provider classes but without the need to specify a crypto
@ -164,7 +165,7 @@ final class RsaKeyHelper { @@ -164,7 +165,7 @@ final class RsaKeyHelper {
private static final Pattern SSH_PUB_KEY = Pattern.compile("ssh-(rsa|dsa) ([A-Za-z0-9/+]+=*) (.*)");
private static RSAPublicKey extractPublicKey(String key) {
private static @Nullable RSAPublicKey extractPublicKey(String key) {
Matcher m = SSH_PUB_KEY.matcher(key);

16
crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaRawEncryptor.java

@ -28,6 +28,8 @@ import java.util.Base64; @@ -28,6 +28,8 @@ import java.util.Base64;
import javax.crypto.Cipher;
import org.jspecify.annotations.Nullable;
/**
* @author Dave Syer
* @since 6.3
@ -42,7 +44,7 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol @@ -42,7 +44,7 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol
private RSAPublicKey publicKey;
private RSAPrivateKey privateKey;
private @Nullable RSAPrivateKey privateKey;
private Charset defaultCharset;
@ -70,11 +72,12 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol @@ -70,11 +72,12 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol
this(DEFAULT_ENCODING, publicKey, null);
}
public RsaRawEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey) {
public RsaRawEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey) {
this(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT);
}
public RsaRawEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, RsaAlgorithm algorithm) {
public RsaRawEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey,
RsaAlgorithm algorithm) {
this.charset = Charset.forName(encoding);
this.publicKey = (RSAPublicKey) publicKey;
this.privateKey = (RSAPrivateKey) privateKey;
@ -135,7 +138,7 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol @@ -135,7 +138,7 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol
}
}
private static byte[] decrypt(byte[] text, RSAPrivateKey key, RsaAlgorithm alg) {
private static byte[] decrypt(byte[] text, @Nullable RSAPrivateKey key, RsaAlgorithm alg) {
ByteArrayOutputStream output = new ByteArrayOutputStream(text.length);
try {
final Cipher cipher = Cipher.getInstance(alg.getJceName());
@ -160,7 +163,10 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol @@ -160,7 +163,10 @@ public class RsaRawEncryptor implements BytesEncryptor, TextEncryptor, RsaKeyHol
}
// copied from sun.security.rsa.RSACore.getByteLength(java.math.BigInteger)
public static int getByteLength(RSAKey key) {
public static int getByteLength(@Nullable RSAKey key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
int n = key.getModulus().bitLength();
return (n + 7) >> 3;
}

15
crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaSecretEncryptor.java

@ -28,6 +28,8 @@ import java.util.Base64; @@ -28,6 +28,8 @@ import java.util.Base64;
import javax.crypto.Cipher;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.keygen.KeyGenerators;
@ -50,7 +52,7 @@ public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKey @@ -50,7 +52,7 @@ public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKey
private final PublicKey publicKey;
private final PrivateKey privateKey;
private final @Nullable PrivateKey privateKey;
private final Charset defaultCharset;
@ -120,16 +122,17 @@ public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKey @@ -120,16 +122,17 @@ public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKey
this(DEFAULT_ENCODING, publicKey, null);
}
public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey) {
public RsaSecretEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey) {
this(encoding, publicKey, privateKey, RsaAlgorithm.DEFAULT);
}
public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, RsaAlgorithm algorithm) {
public RsaSecretEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey,
RsaAlgorithm algorithm) {
this(encoding, publicKey, privateKey, algorithm, DEFAULT_SALT, false);
}
public RsaSecretEncryptor(String encoding, PublicKey publicKey, PrivateKey privateKey, RsaAlgorithm algorithm,
String salt, boolean gcm) {
public RsaSecretEncryptor(String encoding, PublicKey publicKey, @Nullable PrivateKey privateKey,
RsaAlgorithm algorithm, String salt, boolean gcm) {
this.charset = Charset.forName(encoding);
this.publicKey = publicKey;
this.privateKey = privateKey;
@ -206,7 +209,7 @@ public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKey @@ -206,7 +209,7 @@ public class RsaSecretEncryptor implements BytesEncryptor, TextEncryptor, RsaKey
return ((b[0] & 0xFF) << 8) | (b[1] & 0xFF);
}
private static byte[] decrypt(byte[] text, PrivateKey key, RsaAlgorithm alg, String salt, boolean gcm) {
private static byte[] decrypt(byte[] text, @Nullable PrivateKey key, RsaAlgorithm alg, String salt, boolean gcm) {
ByteArrayInputStream input = new ByteArrayInputStream(text);
ByteArrayOutputStream output = new ByteArrayOutputStream(text.length);
try {

20
crypto/src/main/java/org/springframework/security/crypto/encrypt/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.encrypt;
import org.jspecify.annotations.NullMarked;

20
crypto/src/main/java/org/springframework/security/crypto/factory/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.factory;
import org.jspecify.annotations.NullMarked;

20
crypto/src/main/java/org/springframework/security/crypto/keygen/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.keygen;
import org.jspecify.annotations.NullMarked;

16
crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java

@ -19,6 +19,8 @@ package org.springframework.security.crypto.password; @@ -19,6 +19,8 @@ package org.springframework.security.crypto.password;
import java.util.HashMap;
import java.util.Map;
import org.jspecify.annotations.Nullable;
/**
* A password encoder that delegates to another PasswordEncoder based upon a prefixed
* identifier.
@ -146,7 +148,7 @@ public class DelegatingPasswordEncoder implements PasswordEncoder { @@ -146,7 +148,7 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
private final PasswordEncoder passwordEncoderForEncode;
private final Map<String, PasswordEncoder> idToPasswordEncoder;
private final Map<@Nullable String, PasswordEncoder> idToPasswordEncoder;
private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();
@ -232,6 +234,9 @@ public class DelegatingPasswordEncoder implements PasswordEncoder { @@ -232,6 +234,9 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
}
return this.idPrefix + this.idForEncode + this.idSuffix + this.passwordEncoderForEncode.encode(rawPassword);
}
@ -249,7 +254,7 @@ public class DelegatingPasswordEncoder implements PasswordEncoder { @@ -249,7 +254,7 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
return delegate.matches(rawPassword, encodedPassword);
}
private String extractId(String prefixEncodedPassword) {
private @Nullable String extractId(@Nullable String prefixEncodedPassword) {
if (prefixEncodedPassword == null) {
return null;
}
@ -265,14 +270,17 @@ public class DelegatingPasswordEncoder implements PasswordEncoder { @@ -265,14 +270,17 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
}
@Override
public boolean upgradeEncoding(String prefixEncodedPassword) {
public boolean upgradeEncoding(@Nullable String prefixEncodedPassword) {
if (prefixEncodedPassword == null) {
return false;
}
String id = extractId(prefixEncodedPassword);
if (!this.idForEncode.equalsIgnoreCase(id)) {
return true;
}
else {
String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
return this.idToPasswordEncoder.get(id).upgradeEncoding(encodedPassword);
return this.passwordEncoderForEncode.upgradeEncoding(encodedPassword);
}
}

16
crypto/src/main/java/org/springframework/security/crypto/password/LdapShaPasswordEncoder.java

@ -20,6 +20,8 @@ import java.security.MessageDigest; @@ -20,6 +20,8 @@ import java.security.MessageDigest;
import java.util.Base64;
import java.util.Locale;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
@ -72,7 +74,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -72,7 +74,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
this.saltGenerator = saltGenerator;
}
private byte[] combineHashAndSalt(byte[] hash, byte[] salt) {
private byte[] combineHashAndSalt(byte[] hash, byte @Nullable [] salt) {
if (salt == null) {
return hash;
}
@ -96,7 +98,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -96,7 +98,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
return encode(rawPass, salt);
}
private String encode(CharSequence rawPassword, byte[] salt) {
private String encode(@Nullable CharSequence rawPassword, byte @Nullable [] salt) {
MessageDigest sha = getSha(rawPassword);
if (salt != null) {
sha.update(salt);
@ -106,7 +108,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -106,7 +108,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
return prefix + Utf8.decode(Base64.getEncoder().encode(hash));
}
private MessageDigest getSha(CharSequence rawPassword) {
private MessageDigest getSha(@Nullable CharSequence rawPassword) {
try {
MessageDigest sha = MessageDigest.getInstance("SHA");
sha.update(Utf8.encode(rawPassword));
@ -117,7 +119,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -117,7 +119,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
}
}
private String getPrefix(byte[] salt) {
private String getPrefix(byte @Nullable [] salt) {
if (salt == null || salt.length == 0) {
return this.forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
}
@ -145,7 +147,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -145,7 +147,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
return matches((rawPassword != null) ? rawPassword.toString() : null, encodedPassword);
}
private boolean matches(String rawPassword, String encodedPassword) {
private boolean matches(@Nullable String rawPassword, String encodedPassword) {
String prefix = extractPrefix(encodedPassword);
if (prefix == null) {
return PasswordEncoderUtils.equals(encodedPassword, rawPassword);
@ -156,7 +158,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -156,7 +158,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
return PasswordEncoderUtils.equals(encodedRawPass, encodedPassword.substring(startOfHash));
}
private byte[] getSalt(String encodedPassword, String prefix) {
private byte @Nullable [] getSalt(String encodedPassword, String prefix) {
if (prefix.equals(SSHA_PREFIX) || prefix.equals(SSHA_PREFIX_LC)) {
return extractSalt(encodedPassword);
}
@ -170,7 +172,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder { @@ -170,7 +172,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
/**
* Returns the hash prefix or null if there isn't one.
*/
private String extractPrefix(String encPass) {
private @Nullable String extractPrefix(String encPass) {
if (!encPass.startsWith("{")) {
return null;
}

6
crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoderUtils.java

@ -18,6 +18,8 @@ package org.springframework.security.crypto.password; @@ -18,6 +18,8 @@ package org.springframework.security.crypto.password;
import java.security.MessageDigest;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.codec.Utf8;
/**
@ -36,13 +38,13 @@ final class PasswordEncoderUtils { @@ -36,13 +38,13 @@ final class PasswordEncoderUtils {
* @param actual
* @return
*/
static boolean equals(String expected, String actual) {
static boolean equals(String expected, @Nullable String actual) {
byte[] expectedBytes = bytesUtf8(expected);
byte[] actualBytes = bytesUtf8(actual);
return MessageDigest.isEqual(expectedBytes, actualBytes);
}
private static byte[] bytesUtf8(String s) {
private static byte @Nullable [] bytesUtf8(@Nullable String s) {
// need to check if Utf8.encode() runs in constant time (probably not).
// This may leak length of string.
return (s != null) ? Utf8.encode(s) : null;

20
crypto/src/main/java/org/springframework/security/crypto/password/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.password;
import org.jspecify.annotations.NullMarked;

20
crypto/src/main/java/org/springframework/security/crypto/scrypt/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.scrypt;
import org.jspecify.annotations.NullMarked;

20
crypto/src/main/java/org/springframework/security/crypto/util/package-info.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NullMarked
package org.springframework.security.crypto.util;
import org.jspecify.annotations.NullMarked;
Loading…
Cancel
Save