Add Nullability to spring-security-crypto
Closes gh-17533
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
plugins {
|
||||
id 'security-nullability'
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
+5
-4
@@ -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 {
|
||||
|
||||
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 {
|
||||
* @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 {
|
||||
* @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 {
|
||||
* @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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
/**
|
||||
* 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()];
|
||||
|
||||
@@ -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;
|
||||
|
||||
+6
-3
@@ -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 {
|
||||
* @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 {
|
||||
* @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 {
|
||||
* {@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();
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
* 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);
|
||||
|
||||
+3
-1
@@ -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 {
|
||||
|
||||
private final char[] password;
|
||||
|
||||
private KeyStore store;
|
||||
private @Nullable KeyStore store;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
+11
-5
@@ -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
|
||||
|
||||
private RSAPublicKey publicKey;
|
||||
|
||||
private RSAPrivateKey privateKey;
|
||||
private @Nullable RSAPrivateKey privateKey;
|
||||
|
||||
private Charset defaultCharset;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
+9
-6
@@ -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
|
||||
|
||||
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
|
||||
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
|
||||
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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
+12
-4
@@ -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 {
|
||||
|
||||
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 {
|
||||
|
||||
@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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
+4
-2
@@ -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 {
|
||||
* @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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user