Add nullability to spring-security-core
Closes gh-17534
This commit is contained in:
+5
-13
@@ -23,7 +23,7 @@ import org.bouncycastle.crypto.params.Argon2Parameters;
|
||||
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -44,7 +44,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
* @author Simeon Macke
|
||||
* @since 5.3
|
||||
*/
|
||||
public class Argon2PasswordEncoder implements PasswordEncoder {
|
||||
public class Argon2PasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final int DEFAULT_SALT_LENGTH = 16;
|
||||
|
||||
@@ -108,7 +108,7 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
byte[] hash = new byte[this.hashLength];
|
||||
// @formatter:off
|
||||
@@ -127,11 +127,7 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
if (encodedPassword == null) {
|
||||
this.logger.warn("password hash is null");
|
||||
return false;
|
||||
}
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
Argon2EncodingUtils.Argon2Hash decoded;
|
||||
try {
|
||||
decoded = Argon2EncodingUtils.decode(encodedPassword);
|
||||
@@ -148,11 +144,7 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upgradeEncoding(String encodedPassword) {
|
||||
if (encodedPassword == null || encodedPassword.length() == 0) {
|
||||
this.logger.warn("password hash is null");
|
||||
return false;
|
||||
}
|
||||
protected boolean upgradeEncodingNonNull(String encodedPassword) {
|
||||
Argon2Parameters parameters = Argon2EncodingUtils.decode(encodedPassword).getParameters();
|
||||
return parameters.getMemory() < this.memory || parameters.getIterations() < this.iterations;
|
||||
}
|
||||
|
||||
+5
-19
@@ -24,7 +24,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients
|
||||
@@ -34,7 +34,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class BCryptPasswordEncoder implements PasswordEncoder {
|
||||
public class BCryptPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
|
||||
|
||||
@@ -103,10 +103,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
if (rawPassword == null) {
|
||||
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||
}
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
String salt = getSalt();
|
||||
return BCrypt.hashpw(rawPassword.toString(), salt);
|
||||
}
|
||||
@@ -119,14 +116,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
if (rawPassword == null) {
|
||||
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||
}
|
||||
if (encodedPassword == null || encodedPassword.length() == 0) {
|
||||
this.logger.warn("Empty encoded password");
|
||||
return false;
|
||||
}
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
|
||||
this.logger.warn("Encoded password does not look like BCrypt");
|
||||
return false;
|
||||
@@ -135,11 +125,7 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upgradeEncoding(String encodedPassword) {
|
||||
if (encodedPassword == null || encodedPassword.length() == 0) {
|
||||
this.logger.warn("Empty encoded password");
|
||||
return false;
|
||||
}
|
||||
protected boolean upgradeEncodingNonNull(String encodedPassword) {
|
||||
Matcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Encoded password does not look like BCrypt: " + encodedPassword);
|
||||
|
||||
@@ -22,8 +22,6 @@ 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>
|
||||
@@ -41,7 +39,10 @@ public final class Utf8 {
|
||||
/**
|
||||
* Get the bytes of the String in UTF-8 encoded form.
|
||||
*/
|
||||
public static byte[] encode(@Nullable CharSequence string) {
|
||||
public static byte[] encode(CharSequence string) {
|
||||
if (string == null) {
|
||||
throw new IllegalArgumentException("String cannot be null");
|
||||
}
|
||||
try {
|
||||
ByteBuffer bytes = CHARSET.newEncoder().encode(CharBuffer.wrap(string));
|
||||
byte[] bytesCopy = new byte[bytes.limit()];
|
||||
|
||||
+7
-7
@@ -28,7 +28,7 @@ import org.springframework.security.crypto.util.EncodingUtils;
|
||||
*
|
||||
* @author Rob Worsnop
|
||||
*/
|
||||
public abstract class AbstractPasswordEncoder implements PasswordEncoder {
|
||||
public abstract class AbstractPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private final BytesKeyGenerator saltGenerator;
|
||||
|
||||
@@ -37,29 +37,29 @@ public abstract class AbstractPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
byte[] encoded = encodeAndConcatenate(rawPassword, salt);
|
||||
return String.valueOf(Hex.encode(encoded));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
byte[] digested = Hex.decode(encodedPassword);
|
||||
byte[] salt = EncodingUtils.subArray(digested, 0, this.saltGenerator.getKeyLength());
|
||||
return matches(digested, encodeAndConcatenate(rawPassword, salt));
|
||||
return matchesNonNull(digested, encodeAndConcatenate(rawPassword, salt));
|
||||
}
|
||||
|
||||
protected abstract byte[] encode(CharSequence rawPassword, byte[] salt);
|
||||
protected abstract byte[] encodedNonNullPassword(CharSequence rawPassword, byte[] salt);
|
||||
|
||||
protected byte[] encodeAndConcatenate(CharSequence rawPassword, byte[] salt) {
|
||||
return EncodingUtils.concatenate(salt, encode(rawPassword, salt));
|
||||
return EncodingUtils.concatenate(salt, encodedNonNullPassword(rawPassword, salt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant time comparison to prevent against timing attacks.
|
||||
*/
|
||||
protected static boolean matches(byte[] expected, byte[] actual) {
|
||||
protected static boolean matchesNonNull(byte[] expected, byte[] actual) {
|
||||
return MessageDigest.isEqual(expected, actual);
|
||||
}
|
||||
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public abstract class AbstractValidatingPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
@Override
|
||||
public final @Nullable String encode(@Nullable CharSequence rawPassword) {
|
||||
if (rawPassword == null) {
|
||||
return null;
|
||||
}
|
||||
return encodeNonNullPassword(rawPassword.toString());
|
||||
}
|
||||
|
||||
protected abstract String encodeNonNullPassword(String rawPassword);
|
||||
|
||||
@Override
|
||||
public final boolean matches(@Nullable CharSequence rawPassword, @Nullable String encodedPassword) {
|
||||
if (rawPassword == null || rawPassword.length() == 0 || encodedPassword == null
|
||||
|| encodedPassword.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
return matchesNonNull(rawPassword.toString(), encodedPassword);
|
||||
}
|
||||
|
||||
protected abstract boolean matchesNonNull(String rawPassword, String encodedPassword);
|
||||
|
||||
@Override
|
||||
public final boolean upgradeEncoding(@Nullable String encodedPassword) {
|
||||
if (encodedPassword == null || encodedPassword.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
return upgradeEncodingNonNull(encodedPassword);
|
||||
}
|
||||
|
||||
protected boolean upgradeEncodingNonNull(String encodedPassword) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-16
@@ -125,7 +125,7 @@ import org.jspecify.annotations.Nullable;
|
||||
* @since 5.0
|
||||
* @see org.springframework.security.crypto.factory.PasswordEncoderFactories
|
||||
*/
|
||||
public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
public class DelegatingPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final String DEFAULT_ID_PREFIX = "{";
|
||||
|
||||
@@ -233,18 +233,12 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
if (rawPassword == null) {
|
||||
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||
}
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
return this.idPrefix + this.idForEncode + this.idSuffix + this.passwordEncoderForEncode.encode(rawPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
|
||||
if (rawPassword == null && prefixEncodedPassword == null) {
|
||||
return true;
|
||||
}
|
||||
protected boolean matchesNonNull(String rawPassword, String prefixEncodedPassword) {
|
||||
String id = extractId(prefixEncodedPassword);
|
||||
PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
|
||||
if (delegate == null) {
|
||||
@@ -270,10 +264,7 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upgradeEncoding(@Nullable String prefixEncodedPassword) {
|
||||
if (prefixEncodedPassword == null) {
|
||||
return false;
|
||||
}
|
||||
protected boolean upgradeEncodingNonNull(String prefixEncodedPassword) {
|
||||
String id = extractId(prefixEncodedPassword);
|
||||
if (!this.idForEncode.equalsIgnoreCase(id)) {
|
||||
return true;
|
||||
@@ -293,15 +284,15 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
* Default {@link PasswordEncoder} that throws an exception telling that a suitable
|
||||
* {@link PasswordEncoder} for the id could not be found.
|
||||
*/
|
||||
private class UnmappedIdPasswordEncoder implements PasswordEncoder {
|
||||
private class UnmappedIdPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
throw new UnsupportedOperationException("encode is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String prefixEncodedPassword) {
|
||||
String id = extractId(prefixEncodedPassword);
|
||||
if (id != null && !id.isBlank()) {
|
||||
throw new IllegalArgumentException(String.format(NO_PASSWORD_ENCODER_MAPPED, id));
|
||||
|
||||
+7
-11
@@ -46,7 +46,7 @@ import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
* indicate that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
public class LdapShaPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
/** The number of bytes in a SHA hash */
|
||||
private static final int SHA_LENGTH = 20;
|
||||
@@ -88,17 +88,17 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
* Calculates the hash of password (and salt bytes, if supplied) and returns a base64
|
||||
* encoded concatenation of the hash and salt, prefixed with {SHA} (or {SSHA} if salt
|
||||
* was used).
|
||||
* @param rawPass the password to be encoded.
|
||||
* @param rawPassword the password to be encoded.
|
||||
* @return the encoded password in the specified format
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public String encode(CharSequence rawPass) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
return encode(rawPass, salt);
|
||||
return encode(rawPassword, salt);
|
||||
}
|
||||
|
||||
private String encode(@Nullable CharSequence rawPassword, byte @Nullable [] salt) {
|
||||
private String encode(CharSequence rawPassword, byte @Nullable [] salt) {
|
||||
MessageDigest sha = getSha(rawPassword);
|
||||
if (salt != null) {
|
||||
sha.update(salt);
|
||||
@@ -108,7 +108,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
return prefix + Utf8.decode(Base64.getEncoder().encode(hash));
|
||||
}
|
||||
|
||||
private MessageDigest getSha(@Nullable CharSequence rawPassword) {
|
||||
private MessageDigest getSha(CharSequence rawPassword) {
|
||||
try {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA");
|
||||
sha.update(Utf8.encode(rawPassword));
|
||||
@@ -143,11 +143,7 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
||||
* @return true if they match (independent of the case of the prefix).
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
return matches((rawPassword != null) ? rawPassword.toString() : null, encodedPassword);
|
||||
}
|
||||
|
||||
private boolean matches(@Nullable String rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
String prefix = extractPrefix(encodedPassword);
|
||||
if (prefix == null) {
|
||||
return PasswordEncoderUtils.equals(encodedPassword, rawPassword);
|
||||
|
||||
+5
-5
@@ -78,7 +78,7 @@ import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||
* indicate that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Md4PasswordEncoder implements PasswordEncoder {
|
||||
public class Md4PasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final String PREFIX = "{";
|
||||
|
||||
@@ -100,7 +100,7 @@ public class Md4PasswordEncoder implements PasswordEncoder {
|
||||
* encodeHashAsBase64 is enabled.
|
||||
*/
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
public String encodeNonNullPassword(String rawPassword) {
|
||||
String salt = PREFIX + this.saltGenerator.generateKey() + SUFFIX;
|
||||
return digest(salt, rawPassword);
|
||||
}
|
||||
@@ -114,11 +114,11 @@ public class Md4PasswordEncoder implements PasswordEncoder {
|
||||
Md4 md4 = new Md4();
|
||||
md4.update(saltedPasswordBytes, 0, saltedPasswordBytes.length);
|
||||
byte[] digest = md4.digest();
|
||||
String encoded = encode(digest);
|
||||
String encoded = encodedNonNullPassword(digest);
|
||||
return salt + encoded;
|
||||
}
|
||||
|
||||
private String encode(byte[] digest) {
|
||||
private String encodedNonNullPassword(byte[] digest) {
|
||||
if (this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.getEncoder().encode(digest));
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class Md4PasswordEncoder implements PasswordEncoder {
|
||||
* @return true or false
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
String salt = extractSalt(encodedPassword);
|
||||
String rawPasswordEncoded = digest(salt, rawPassword);
|
||||
return PasswordEncoderUtils.equals(encodedPassword.toString(), rawPasswordEncoded);
|
||||
|
||||
+5
-5
@@ -82,7 +82,7 @@ import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||
* indicate that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public class MessageDigestPasswordEncoder implements PasswordEncoder {
|
||||
public class MessageDigestPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final String PREFIX = "{";
|
||||
|
||||
@@ -116,7 +116,7 @@ public class MessageDigestPasswordEncoder implements PasswordEncoder {
|
||||
* encodeHashAsBase64 is enabled.
|
||||
*/
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
String salt = PREFIX + this.saltGenerator.generateKey() + SUFFIX;
|
||||
return digest(salt, rawPassword);
|
||||
}
|
||||
@@ -124,11 +124,11 @@ public class MessageDigestPasswordEncoder implements PasswordEncoder {
|
||||
private String digest(String salt, CharSequence rawPassword) {
|
||||
String saltedPassword = rawPassword + salt;
|
||||
byte[] digest = this.digester.digest(Utf8.encode(saltedPassword));
|
||||
String encoded = encode(digest);
|
||||
String encoded = encodedNonNullPassword(digest);
|
||||
return salt + encoded;
|
||||
}
|
||||
|
||||
private String encode(byte[] digest) {
|
||||
private String encodedNonNullPassword(byte[] digest) {
|
||||
if (this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.getEncoder().encode(digest));
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public class MessageDigestPasswordEncoder implements PasswordEncoder {
|
||||
* @return true or false
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
String salt = extractSalt(encodedPassword);
|
||||
String rawPasswordEncoded = digest(salt, rawPassword);
|
||||
return PasswordEncoderUtils.equals(encodedPassword.toString(), rawPasswordEncoded);
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@ package org.springframework.security.crypto.password;
|
||||
* legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class NoOpPasswordEncoder implements PasswordEncoder {
|
||||
public final class NoOpPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final PasswordEncoder INSTANCE = new NoOpPasswordEncoder();
|
||||
|
||||
@@ -39,12 +39,12 @@ public final class NoOpPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
return rawPassword.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
return rawPassword.toString().equals(encodedPassword);
|
||||
}
|
||||
|
||||
|
||||
+21
-11
@@ -16,40 +16,50 @@
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Service interface for encoding passwords.
|
||||
*
|
||||
* The preferred implementation is {@code BCryptPasswordEncoder}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface PasswordEncoder {
|
||||
|
||||
/**
|
||||
* Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or
|
||||
* greater hash combined with an 8-byte or greater randomly generated salt.
|
||||
* Encode the raw password. Generally, a good encoding algorithm uses an adaptive one
|
||||
* way function.
|
||||
* @param rawPassword a password that has not been encoded. The value can be null in
|
||||
* the event that the user has no password; in which case the result must be null.
|
||||
* @return A non-null encoded password, unless the rawPassword was null in which case
|
||||
* the result must be null.
|
||||
*/
|
||||
String encode(CharSequence rawPassword);
|
||||
@Nullable String encode(@Nullable CharSequence rawPassword);
|
||||
|
||||
/**
|
||||
* Verify the encoded password obtained from storage matches the submitted raw
|
||||
* password after it too is encoded. Returns true if the passwords match, false if
|
||||
* they do not. The stored password itself is never decoded.
|
||||
* @param rawPassword the raw password to encode and match
|
||||
* @param encodedPassword the encoded password from storage to compare with
|
||||
* they do not. The stored password itself is never decoded. Never true if either
|
||||
* rawPassword or encodedPassword is null or an empty String.
|
||||
* @param rawPassword the raw password to encode and match.
|
||||
* @param encodedPassword the encoded password from storage to compare with.
|
||||
* @return true if the raw password, after encoding, matches the encoded password from
|
||||
* storage
|
||||
* storage.
|
||||
*/
|
||||
boolean matches(CharSequence rawPassword, String encodedPassword);
|
||||
boolean matches(@Nullable CharSequence rawPassword, @Nullable String encodedPassword);
|
||||
|
||||
/**
|
||||
* Returns true if the encoded password should be encoded again for better security,
|
||||
* else false. The default implementation always returns false.
|
||||
* @param encodedPassword the encoded password to check
|
||||
* @param encodedPassword the encoded password to check. Possibly null if the user did
|
||||
* not have a password.
|
||||
* @return true if the encoded password should be encoded again for better security,
|
||||
* else false.
|
||||
* else false. If encodedPassword is null (the user didn't have a password), then
|
||||
* always false.
|
||||
*/
|
||||
default boolean upgradeEncoding(String encodedPassword) {
|
||||
default boolean upgradeEncoding(@Nullable String encodedPassword) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -46,7 +46,7 @@ import org.springframework.security.crypto.util.EncodingUtils;
|
||||
* @author Loïc Guibert
|
||||
* @since 4.1
|
||||
*/
|
||||
public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
public class Pbkdf2PasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final int DEFAULT_SALT_LENGTH = 16;
|
||||
|
||||
@@ -194,13 +194,13 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
byte[] encoded = encode(rawPassword, salt);
|
||||
return encode(encoded);
|
||||
byte[] encoded = encodedNonNullPassword(rawPassword, salt);
|
||||
return encodedNonNullPassword(encoded);
|
||||
}
|
||||
|
||||
private String encode(byte[] bytes) {
|
||||
private String encodedNonNullPassword(byte[] bytes) {
|
||||
if (this.encodeHashAsBase64) {
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
}
|
||||
@@ -208,10 +208,10 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
byte[] digested = decode(encodedPassword);
|
||||
byte[] salt = EncodingUtils.subArray(digested, 0, this.saltGenerator.getKeyLength());
|
||||
return MessageDigest.isEqual(digested, encode(rawPassword, salt));
|
||||
return MessageDigest.isEqual(digested, encodedNonNullPassword(rawPassword, salt));
|
||||
}
|
||||
|
||||
private byte[] decode(String encodedBytes) {
|
||||
@@ -221,7 +221,7 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
return Hex.decode(encodedBytes);
|
||||
}
|
||||
|
||||
private byte[] encode(CharSequence rawPassword, byte[] salt) {
|
||||
private byte[] encodedNonNullPassword(CharSequence rawPassword, byte[] salt) {
|
||||
try {
|
||||
PBEKeySpec spec = new PBEKeySpec(rawPassword.toString().toCharArray(),
|
||||
EncodingUtils.concatenate(salt, this.secret), this.iterations, this.hashWidth);
|
||||
|
||||
+5
-5
@@ -48,7 +48,7 @@ import org.springframework.security.crypto.util.EncodingUtils;
|
||||
* indicate that this is a legacy implementation and using it is considered insecure.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class StandardPasswordEncoder implements PasswordEncoder {
|
||||
public final class StandardPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final int DEFAULT_ITERATIONS = 1024;
|
||||
|
||||
@@ -75,12 +75,12 @@ public final class StandardPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return encode(rawPassword, this.saltGenerator.generateKey());
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
return encodedNonNullPassword(rawPassword, this.saltGenerator.generateKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
byte[] digested = decode(encodedPassword);
|
||||
byte[] salt = EncodingUtils.subArray(digested, 0, this.saltGenerator.getKeyLength());
|
||||
return MessageDigest.isEqual(digested, digest(rawPassword, salt));
|
||||
@@ -92,7 +92,7 @@ public final class StandardPasswordEncoder implements PasswordEncoder {
|
||||
this.saltGenerator = KeyGenerators.secureRandom();
|
||||
}
|
||||
|
||||
private String encode(CharSequence rawPassword, byte[] salt) {
|
||||
private String encodedNonNullPassword(CharSequence rawPassword, byte[] salt) {
|
||||
byte[] digest = digest(rawPassword, salt);
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
|
||||
+5
-12
@@ -26,7 +26,7 @@ import org.bouncycastle.crypto.generators.SCrypt;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -56,7 +56,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
public class SCryptPasswordEncoder extends AbstractValidatingPasswordEncoder {
|
||||
|
||||
private static final int DEFAULT_CPU_COST = 65536;
|
||||
|
||||
@@ -146,24 +146,17 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
return digest(rawPassword, this.saltGenerator.generateKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
if (encodedPassword == null || encodedPassword.length() < this.keyLength) {
|
||||
this.logger.warn("Empty encoded password");
|
||||
return false;
|
||||
}
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
return decodeAndCheckMatches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upgradeEncoding(String encodedPassword) {
|
||||
if (encodedPassword == null || encodedPassword.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
protected boolean upgradeEncodingNonNull(String encodedPassword) {
|
||||
String[] parts = encodedPassword.split("\\$");
|
||||
if (parts.length != 4) {
|
||||
throw new IllegalArgumentException("Encoded password does not look like SCrypt: " + encodedPassword);
|
||||
|
||||
+48
-42
@@ -19,6 +19,7 @@ package org.springframework.security.crypto.argon2;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
@@ -26,6 +27,7 @@ import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.password.AbstractPasswordEncoderValidationTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -34,56 +36,59 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Simeon Macke
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class Argon2PasswordEncoderTests {
|
||||
public class Argon2PasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@Mock
|
||||
private BytesKeyGenerator keyGeneratorMock;
|
||||
|
||||
private Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeDoesNotEqualPassword() {
|
||||
String result = this.encoder.encode("password");
|
||||
public void encodedNonNullPasswordDoesNotEqualPassword() {
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(result).isNotEqualTo("password");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenEqualPasswordThenMatches() {
|
||||
String result = this.encoder.encode("password");
|
||||
assertThat(this.encoder.matches("password", result)).isTrue();
|
||||
public void encodedNonNullPasswordWhenEqualPasswordThenMatches() {
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenEqualWithUnicodeThenMatches() {
|
||||
String result = this.encoder.encode("passw\u9292rd");
|
||||
assertThat(this.encoder.matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(this.encoder.matches("passw\u9292rd", result)).isTrue();
|
||||
public void encodedNonNullPasswordWhenEqualWithUnicodeThenMatches() {
|
||||
String result = getEncoder().encode("passw\u9292rd");
|
||||
assertThat(getEncoder().matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(getEncoder().matches("passw\u9292rd", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenNotEqualThenNotMatches() {
|
||||
String result = this.encoder.encode("password");
|
||||
assertThat(this.encoder.matches("bogus", result)).isFalse();
|
||||
public void encodedNonNullPasswordWhenNotEqualThenNotMatches() {
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("bogus", result)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenEqualPasswordWithCustomParamsThenMatches() {
|
||||
this.encoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);
|
||||
String result = this.encoder.encode("password");
|
||||
assertThat(this.encoder.matches("password", result)).isTrue();
|
||||
public void encodedNonNullPasswordWhenEqualPasswordWithCustomParamsThenMatches() {
|
||||
setEncoder(new Argon2PasswordEncoder(20, 64, 4, 256, 4));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenRanTwiceThenResultsNotEqual() {
|
||||
public void encodedNonNullPasswordWhenRanTwiceThenResultsNotEqual() {
|
||||
String password = "secret";
|
||||
assertThat(this.encoder.encode(password)).isNotEqualTo(this.encoder.encode(password));
|
||||
assertThat(getEncoder().encode(password)).isNotEqualTo(getEncoder().encode(password));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenRanTwiceWithCustomParamsThenNotEquals() {
|
||||
this.encoder = new Argon2PasswordEncoder(20, 64, 4, 256, 4);
|
||||
public void encodedNonNullPasswordWhenRanTwiceWithCustomParamsThenNotEquals() {
|
||||
setEncoder(new Argon2PasswordEncoder(20, 64, 4, 256, 4));
|
||||
String password = "secret";
|
||||
assertThat(this.encoder.encode(password)).isNotEqualTo(this.encoder.encode(password));
|
||||
assertThat(getEncoder().encode(password)).isNotEqualTo(getEncoder().encode(password));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,55 +102,56 @@ public class Argon2PasswordEncoderTests {
|
||||
|
||||
@Test
|
||||
public void matchesWhenEncodedPassIsNullThenFalse() {
|
||||
assertThat(this.encoder.matches("password", null)).isFalse();
|
||||
assertThat(getEncoder().matches("password", null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenEncodedPassIsEmptyThenFalse() {
|
||||
assertThat(this.encoder.matches("password", "")).isFalse();
|
||||
assertThat(getEncoder().matches("password", "")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenEncodedPassIsBogusThenFalse() {
|
||||
assertThat(this.encoder.matches("password", "012345678901234567890123456789")).isFalse();
|
||||
assertThat(getEncoder().matches("password", "012345678901234567890123456789")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenUsingPredictableSaltThenEqualTestHash() throws Exception {
|
||||
public void encodedNonNullPasswordWhenUsingPredictableSaltThenEqualTestHash() throws Exception {
|
||||
injectPredictableSaltGen();
|
||||
String hash = this.encoder.encode("sometestpassword");
|
||||
String hash = getEncoder().encode("sometestpassword");
|
||||
assertThat(hash).isEqualTo(
|
||||
"$argon2id$v=19$m=4096,t=3,p=1$QUFBQUFBQUFBQUFBQUFBQQ$hmmTNyJlwbb6HAvFoHFWF+u03fdb0F2qA+39oPlcAqo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenUsingPredictableSaltWithCustomParamsThenEqualTestHash() throws Exception {
|
||||
this.encoder = new Argon2PasswordEncoder(16, 32, 4, 512, 5);
|
||||
public void encodedNonNullPasswordWhenUsingPredictableSaltWithCustomParamsThenEqualTestHash() throws Exception {
|
||||
setEncoder(new Argon2PasswordEncoder(16, 32, 4, 512, 5));
|
||||
injectPredictableSaltGen();
|
||||
String hash = this.encoder.encode("sometestpassword");
|
||||
String hash = getEncoder().encode("sometestpassword");
|
||||
assertThat(hash).isEqualTo(
|
||||
"$argon2id$v=19$m=512,t=5,p=4$QUFBQUFBQUFBQUFBQUFBQQ$PNv4C3K50bz3rmON+LtFpdisD7ePieLNq+l5iUHgc1k");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenUsingPredictableSaltWithDefaultsForSpringSecurity_v5_8ThenEqualTestHash() throws Exception {
|
||||
this.encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
|
||||
public void encodedNonNullPasswordWhenUsingPredictableSaltWithDefaultsForSpringSecurity_v5_8ThenEqualTestHash()
|
||||
throws Exception {
|
||||
setEncoder(Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
|
||||
injectPredictableSaltGen();
|
||||
String hash = this.encoder.encode("sometestpassword");
|
||||
String hash = getEncoder().encode("sometestpassword");
|
||||
assertThat(hash).isEqualTo(
|
||||
"$argon2id$v=19$m=16384,t=2,p=1$QUFBQUFBQUFBQUFBQUFBQQ$zGt5MiNPSUOo4/7jBcJMayCPfcsLJ4c0WUxhwGDIYPw");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenSameEncodingThenFalse() {
|
||||
String hash = this.encoder.encode("password");
|
||||
assertThat(this.encoder.upgradeEncoding(hash)).isFalse();
|
||||
String hash = getEncoder().encode("password");
|
||||
assertThat(getEncoder().upgradeEncoding(hash)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenSameStandardParamsThenFalse() {
|
||||
Argon2PasswordEncoder newEncoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();
|
||||
String hash = this.encoder.encode("password");
|
||||
String hash = getEncoder().encode("password");
|
||||
assertThat(newEncoder.upgradeEncoding(hash)).isFalse();
|
||||
}
|
||||
|
||||
@@ -183,17 +189,17 @@ public class Argon2PasswordEncoderTests {
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenEncodedPassIsNullThenFalse() {
|
||||
assertThat(this.encoder.upgradeEncoding(null)).isFalse();
|
||||
assertThat(getEncoder().upgradeEncoding(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenEncodedPassIsEmptyThenFalse() {
|
||||
assertThat(this.encoder.upgradeEncoding("")).isFalse();
|
||||
assertThat(getEncoder().upgradeEncoding("")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenEncodedPassIsBogusThenThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.encoder.upgradeEncoding("thisIsNoValidHash"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getEncoder().upgradeEncoding("thisIsNoValidHash"));
|
||||
}
|
||||
|
||||
private void injectPredictableSaltGen() throws Exception {
|
||||
@@ -203,9 +209,9 @@ public class Argon2PasswordEncoderTests {
|
||||
// we can't use the @InjectMock-annotation because the salt-generator is set in
|
||||
// the constructor
|
||||
// and Mockito will only inject mocks if they are null
|
||||
Field saltGen = this.encoder.getClass().getDeclaredField("saltGenerator");
|
||||
Field saltGen = getEncoder().getClass().getDeclaredField("saltGenerator");
|
||||
saltGen.setAccessible(true);
|
||||
saltGen.set(this.encoder, this.keyGeneratorMock);
|
||||
saltGen.set(getEncoder(), this.keyGeneratorMock);
|
||||
saltGen.setAccessible(false);
|
||||
}
|
||||
|
||||
|
||||
+66
-83
@@ -18,8 +18,11 @@ package org.springframework.security.crypto.bcrypt;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.crypto.password.AbstractPasswordEncoderValidationTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
@@ -27,107 +30,107 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BCryptPasswordEncoderTests {
|
||||
public class BCryptPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(new BCryptPasswordEncoder());
|
||||
}
|
||||
|
||||
@Test
|
||||
// gh-5548
|
||||
public void emptyRawPasswordDoesNotMatchPassword() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("", result)).isFalse();
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("", result)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2yMatches() {
|
||||
// $2y is default version
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
String result = encoder.encode("password");
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(result.equals("password")).isFalse();
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2aMatches() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A);
|
||||
String result = encoder.encode("password");
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(result.equals("password")).isFalse();
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2bMatches() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B);
|
||||
String result = encoder.encode("password");
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(result.equals("password")).isFalse();
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2yUnicode() {
|
||||
// $2y is default version
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
String result = encoder.encode("passw\u9292rd");
|
||||
assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(encoder.matches("passw\u9292rd", result)).isTrue();
|
||||
String result = getEncoder().encode("passw\u9292rd");
|
||||
assertThat(getEncoder().matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(getEncoder().matches("passw\u9292rd", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2aUnicode() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A);
|
||||
String result = encoder.encode("passw\u9292rd");
|
||||
assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(encoder.matches("passw\u9292rd", result)).isTrue();
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A));
|
||||
String result = getEncoder().encode("passw\u9292rd");
|
||||
assertThat(getEncoder().matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(getEncoder().matches("passw\u9292rd", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2bUnicode() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B);
|
||||
String result = encoder.encode("passw\u9292rd");
|
||||
assertThat(encoder.matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(encoder.matches("passw\u9292rd", result)).isTrue();
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B));
|
||||
String result = getEncoder().encode("passw\u9292rd");
|
||||
assertThat(getEncoder().matches("pass\u9292\u9292rd", result)).isFalse();
|
||||
assertThat(getEncoder().matches("passw\u9292rd", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2yNotMatches() {
|
||||
// $2y is default version
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("bogus", result)).isFalse();
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("bogus", result)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2aNotMatches() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A);
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("bogus", result)).isFalse();
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("bogus", result)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2bNotMatches() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B);
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("bogus", result)).isFalse();
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("bogus", result)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2yCustomStrength() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(8);
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
setEncoder(new BCryptPasswordEncoder(8));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2aCustomStrength() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A, 8);
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2A, 8));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void $2bCustomStrength() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B, 8);
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
setEncoder(new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B, 8));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,27 +145,25 @@ public class BCryptPasswordEncoderTests {
|
||||
|
||||
@Test
|
||||
public void customRandom() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(8, new SecureRandom());
|
||||
String result = encoder.encode("password");
|
||||
assertThat(encoder.matches("password", result)).isTrue();
|
||||
setEncoder(new BCryptPasswordEncoder(8, new SecureRandom()));
|
||||
String result = getEncoder().encode("password");
|
||||
assertThat(getEncoder().matches("password", result)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesntMatchNullEncodedValue() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThat(encoder.matches("password", null)).isFalse();
|
||||
setEncoder(new BCryptPasswordEncoder());
|
||||
assertThat(getEncoder().matches("password", null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesntMatchEmptyEncodedValue() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThat(encoder.matches("password", "")).isFalse();
|
||||
assertThat(getEncoder().matches("password", "")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesntMatchBogusEncodedValue() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThat(encoder.matches("password", "012345678901234567890123456789")).isFalse();
|
||||
assertThat(getEncoder().matches("password", "012345678901234567890123456789")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,9 +182,8 @@ public class BCryptPasswordEncoderTests {
|
||||
*/
|
||||
@Test
|
||||
public void upgradeFromNullOrEmpty() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThat(encoder.upgradeEncoding(null)).isFalse();
|
||||
assertThat(encoder.upgradeEncoding("")).isFalse();
|
||||
assertThat(getEncoder().upgradeEncoding(null)).isFalse();
|
||||
assertThat(getEncoder().upgradeEncoding("")).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,65 +192,48 @@ public class BCryptPasswordEncoderTests {
|
||||
*/
|
||||
@Test
|
||||
public void upgradeFromNonBCrypt() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> encoder.upgradeEncoding("not-a-bcrypt-password"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeNullRawPassword() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> encoder.encode(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchNullRawPassword() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> encoder.matches(null, "does-not-matter"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getEncoder().upgradeEncoding("not-a-bcrypt-password"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeWhenNoRoundsThenTrue() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThat(encoder.upgradeEncoding("$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue")).isTrue();
|
||||
assertThat(getEncoder().upgradeEncoding("$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue"))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkWhenNoRoundsThenTrue() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
assertThat(encoder.matches("password", "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue"))
|
||||
assertThat(getEncoder().matches("password", "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue"))
|
||||
.isTrue();
|
||||
assertThat(encoder.matches("wrong", "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue")).isFalse();
|
||||
assertThat(getEncoder().matches("wrong", "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue"))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenPasswordOverMaxLengthThenThrowIllegalArgumentException() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
|
||||
String password72chars = "123456789012345678901234567890123456789012345678901234567890123456789012";
|
||||
encoder.encode(password72chars);
|
||||
getEncoder().encode(password72chars);
|
||||
|
||||
String password73chars = password72chars + "3";
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> encoder.encode(password73chars));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getEncoder().encode(password73chars));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenPasswordOverMaxLengthThenAllowToMatch() {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
|
||||
String password71chars = "12345678901234567890123456789012345678901234567890123456789012345678901";
|
||||
String encodedPassword71chars = "$2a$10$jx3x2FaF.iX5QZ9i3O424Os2Ou5P5JrnedmWYHuDyX8JKA4Unp4xq";
|
||||
assertThat(encoder.matches(password71chars, encodedPassword71chars)).isTrue();
|
||||
assertThat(getEncoder().matches(password71chars, encodedPassword71chars)).isTrue();
|
||||
|
||||
String password72chars = password71chars + "2";
|
||||
String encodedPassword72chars = "$2a$10$oXYO6/UvbsH5rQEraBkl6uheccBqdB3n.RaWbrimog9hS2GX4lo/O";
|
||||
assertThat(encoder.matches(password72chars, encodedPassword72chars)).isTrue();
|
||||
assertThat(getEncoder().matches(password72chars, encodedPassword72chars)).isTrue();
|
||||
|
||||
// Max length is 72 bytes, however, we need to ensure backwards compatibility
|
||||
// for previously encoded passwords that are greater than 72 bytes and allow the
|
||||
// match to be performed.
|
||||
String password73chars = password72chars + "3";
|
||||
String encodedPassword73chars = "$2a$10$1l9.kvQTsqNLiCYFqmKtQOHkp.BrgIrwsnTzWo9jdbQRbuBYQ/AVK";
|
||||
assertThat(encoder.matches(password73chars, encodedPassword73chars)).isTrue();
|
||||
assertThat(getEncoder().matches(password73chars, encodedPassword73chars)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
/**
|
||||
* Test {@link AbstractPasswordEncoder} (not intended to be extended).
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see AbstractPasswordEncoderValidationTests
|
||||
*/
|
||||
final class AbstractPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(new AbstractPasswordEncoder() {
|
||||
|
||||
@Override
|
||||
protected byte[] encodedNonNullPassword(CharSequence rawPassword, byte[] salt) {
|
||||
return new byte[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* A base class for other tests to perform validation of the arguments to
|
||||
* {@link PasswordEncoder} instances in a consistent way.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public abstract class AbstractPasswordEncoderValidationTests {
|
||||
|
||||
private PasswordEncoder encoder;
|
||||
|
||||
protected void setEncoder(PasswordEncoder encoder) {
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
protected <T extends PasswordEncoder> T getEncoder(Class<T> clazz) {
|
||||
return getEncoder();
|
||||
}
|
||||
|
||||
protected <T extends PasswordEncoder> T getEncoder() {
|
||||
return (T) this.encoder;
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeWhenNullThenNull() {
|
||||
assertThat(this.encoder.encode(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenEncodedPasswordNullThenFalse() {
|
||||
assertThat(this.encoder.matches("raw", null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenEncodedPasswordEmptyThenFalse() {
|
||||
assertThat(this.encoder.matches("raw", "")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenRawPasswordNullThenFalse() {
|
||||
assertThat(this.encoder.matches(null, this.encoder.encode("password"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenRawPasswordEmptyThenFalse() {
|
||||
assertThat(this.encoder.matches("", this.encoder.encode("password"))).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
/**
|
||||
* Test {@link AbstractValidatingPasswordEncoder}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class AbstractValidatingPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(new AbstractValidatingPasswordEncoder() {
|
||||
@Override
|
||||
protected String encodeNonNullPassword(String rawPassword) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchesNonNull(String rawPassword, String encodedPassword) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+23
-31
@@ -41,7 +41,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
* @since 5.0
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DelegatingPasswordEncoderTests {
|
||||
public class DelegatingPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder bcrypt;
|
||||
@@ -64,8 +64,6 @@ public class DelegatingPasswordEncoderTests {
|
||||
|
||||
private Map<String, PasswordEncoder> delegates;
|
||||
|
||||
private DelegatingPasswordEncoder passwordEncoder;
|
||||
|
||||
private DelegatingPasswordEncoder onlySuffixPasswordEncoder;
|
||||
|
||||
private static final String NO_PASSWORD_ENCODER_MAPPED = "There is no password encoder mapped for the id 'unmapped'. "
|
||||
@@ -81,8 +79,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
this.delegates = new HashMap<>();
|
||||
this.delegates.put(this.bcryptId, this.bcrypt);
|
||||
this.delegates.put("noop", this.noop);
|
||||
this.passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
|
||||
setEncoder(new DelegatingPasswordEncoder(this.bcryptId, this.delegates));
|
||||
this.onlySuffixPasswordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates, "", "$");
|
||||
}
|
||||
|
||||
@@ -149,14 +146,14 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void setDefaultPasswordEncoderForMatchesWhenNullThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.setDefaultPasswordEncoderForMatches(null));
|
||||
.isThrownBy(() -> getEncoder(DelegatingPasswordEncoder.class).setDefaultPasswordEncoderForMatches(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenCustomDefaultPasswordEncoderForMatchesThenDelegates() {
|
||||
String encodedPassword = "{unmapped}" + this.rawPassword;
|
||||
this.passwordEncoder.setDefaultPasswordEncoderForMatches(this.invalidId);
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, encodedPassword)).isFalse();
|
||||
getEncoder(DelegatingPasswordEncoder.class).setDefaultPasswordEncoderForMatches(this.invalidId);
|
||||
assertThat(getEncoder().matches(this.rawPassword, encodedPassword)).isFalse();
|
||||
verify(this.invalidId).matches(this.rawPassword, encodedPassword);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
@@ -164,7 +161,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void encodeWhenValidThenUsesIdForEncode() {
|
||||
given(this.bcrypt.encode(this.rawPassword)).willReturn(this.encodedPassword);
|
||||
assertThat(this.passwordEncoder.encode(this.rawPassword)).isEqualTo(this.bcryptEncodedPassword);
|
||||
assertThat(getEncoder().encode(this.rawPassword)).isEqualTo(this.bcryptEncodedPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -176,7 +173,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenBCryptThenDelegatesToBCrypt() {
|
||||
given(this.bcrypt.matches(this.rawPassword, this.encodedPassword)).willReturn(true);
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.bcryptEncodedPassword)).isTrue();
|
||||
assertThat(getEncoder().matches(this.rawPassword, this.bcryptEncodedPassword)).isTrue();
|
||||
verify(this.bcrypt).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyNoMoreInteractions(this.noop);
|
||||
}
|
||||
@@ -192,7 +189,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenNoopThenDelegatesToNoop() {
|
||||
given(this.noop.matches(this.rawPassword, this.encodedPassword)).willReturn(true);
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.noopEncodedPassword)).isTrue();
|
||||
assertThat(getEncoder().matches(this.rawPassword, this.noopEncodedPassword)).isTrue();
|
||||
verify(this.noop).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyNoMoreInteractions(this.bcrypt);
|
||||
}
|
||||
@@ -200,7 +197,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenUnMappedThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{unmapped}" + this.rawPassword))
|
||||
.isThrownBy(() -> getEncoder().matches(this.rawPassword, "{unmapped}" + this.rawPassword))
|
||||
.withMessage(NO_PASSWORD_ENCODER_MAPPED);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
@@ -208,7 +205,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword))
|
||||
.isThrownBy(() -> getEncoder().matches(this.rawPassword, "{bcrypt" + this.rawPassword))
|
||||
.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
@@ -216,7 +213,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenNoStartingPrefixStringThenFalse() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "bcrypt}" + this.rawPassword))
|
||||
.isThrownBy(() -> getEncoder().matches(this.rawPassword, "bcrypt}" + this.rawPassword))
|
||||
.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
@@ -224,7 +221,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenNoIdStringThenFalse() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{}" + this.rawPassword))
|
||||
.isThrownBy(() -> getEncoder().matches(this.rawPassword, "{}" + this.rawPassword))
|
||||
.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
@@ -232,7 +229,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenPrefixInMiddleThenFalse() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "invalid" + this.bcryptEncodedPassword))
|
||||
.isThrownBy(() -> getEncoder().matches(this.rawPassword, "invalid" + this.bcryptEncodedPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.withMessage(MALFORMED_PASSWORD_ENCODER_PREFIX);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
@@ -251,56 +248,51 @@ public class DelegatingPasswordEncoderTests {
|
||||
@Test
|
||||
public void matchesWhenNullIdThenDelegatesToInvalidId() {
|
||||
this.delegates.put(null, this.invalidId);
|
||||
this.passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
setEncoder(new DelegatingPasswordEncoder(this.bcryptId, this.delegates));
|
||||
given(this.invalidId.matches(this.rawPassword, this.encodedPassword)).willReturn(true);
|
||||
assertThat(this.passwordEncoder.matches(this.rawPassword, this.encodedPassword)).isTrue();
|
||||
assertThat(getEncoder().matches(this.rawPassword, this.encodedPassword)).isTrue();
|
||||
verify(this.invalidId).matches(this.rawPassword, this.encodedPassword);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenRawPasswordNotNullAndEncodedPasswordNullThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenEncodedPasswordNullThenTrue() {
|
||||
assertThat(this.passwordEncoder.upgradeEncoding(null)).isTrue();
|
||||
public void upgradeEncodingWhenEncodedPasswordNullThenFalse() {
|
||||
assertThat(getEncoder().upgradeEncoding(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenNullIdThenTrue() {
|
||||
assertThat(this.passwordEncoder.upgradeEncoding(this.encodedPassword)).isTrue();
|
||||
assertThat(getEncoder().upgradeEncoding(this.encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenIdInvalidFormatThenTrue() {
|
||||
assertThat(this.passwordEncoder.upgradeEncoding("{bcrypt" + this.encodedPassword)).isTrue();
|
||||
assertThat(getEncoder().upgradeEncoding("{bcrypt" + this.encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenSameIdAndEncoderFalseThenEncoderDecidesFalse() {
|
||||
assertThat(this.passwordEncoder.upgradeEncoding(this.bcryptEncodedPassword)).isFalse();
|
||||
assertThat(getEncoder().upgradeEncoding(this.bcryptEncodedPassword)).isFalse();
|
||||
verify(this.bcrypt).upgradeEncoding(this.encodedPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenSameIdAndEncoderTrueThenEncoderDecidesTrue() {
|
||||
given(this.bcrypt.upgradeEncoding(any())).willReturn(true);
|
||||
assertThat(this.passwordEncoder.upgradeEncoding(this.bcryptEncodedPassword)).isTrue();
|
||||
assertThat(getEncoder().upgradeEncoding(this.bcryptEncodedPassword)).isTrue();
|
||||
verify(this.bcrypt).upgradeEncoding(this.encodedPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upgradeEncodingWhenDifferentIdThenTrue() {
|
||||
assertThat(this.passwordEncoder.upgradeEncoding(this.noopEncodedPassword)).isTrue();
|
||||
assertThat(getEncoder().upgradeEncoding(this.noopEncodedPassword)).isTrue();
|
||||
verifyNoMoreInteractions(this.bcrypt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesShouldThrowIllegalArgumentExceptionWhenNoPasswordEncoderIsMappedForTheId() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches("rawPassword", "prefixEncodedPassword"))
|
||||
.isThrownBy(() -> getEncoder().matches("rawPassword", "prefixEncodedPassword"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.withMessage(NO_PASSWORD_ENCODER_PREFIX);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
|
||||
+38
-31
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
@@ -29,19 +30,22 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class LdapShaPasswordEncoderTests {
|
||||
public class LdapShaPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
LdapShaPasswordEncoder sha = new LdapShaPasswordEncoder();
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(new LdapShaPasswordEncoder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidPasswordFails() {
|
||||
assertThat(this.sha.matches("wrongpassword", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isFalse();
|
||||
assertThat(getEncoder().matches("wrongpassword", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidSaltedPasswordFails() {
|
||||
assertThat(this.sha.matches("wrongpassword", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isFalse();
|
||||
assertThat(this.sha.matches("wrongpassword", "{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isFalse();
|
||||
assertThat(getEncoder().matches("wrongpassword", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isFalse();
|
||||
assertThat(getEncoder().matches("wrongpassword", "{SSHA}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,12 +53,13 @@ public class LdapShaPasswordEncoderTests {
|
||||
*/
|
||||
@Test
|
||||
public void validPasswordSucceeds() {
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
LdapShaPasswordEncoder ldap = getEncoder();
|
||||
ldap.setForceLowerCasePrefix(false);
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
ldap.setForceLowerCasePrefix(true);
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SHA}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{sha}ddSFGmjXYPbZC+NXR2kCzBRjqiE=")).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,47 +67,49 @@ public class LdapShaPasswordEncoderTests {
|
||||
*/
|
||||
@Test
|
||||
public void validSaltedPasswordSucceeds() {
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isTrue();
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(this.sha.matches("boabspasswurd", "{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isTrue();
|
||||
LdapShaPasswordEncoder ldap = getEncoder();
|
||||
ldap.setForceLowerCasePrefix(false);
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isTrue();
|
||||
ldap.setForceLowerCasePrefix(true);
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{ssha}PQy2j+6n5ytA+YlAKkM8Fh4p6u2JxfVd")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
// SEC-1031
|
||||
public void fullLengthOfHashIsUsedInComparison() {
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isTrue();
|
||||
// Change the first hash character from '2' to '3'
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}35ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isFalse();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SSHA}35ro4PKC8jhQZ26jVsozhX/xaP0suHgX")).isFalse();
|
||||
// Change the last hash character from 'X' to 'Y'
|
||||
assertThat(this.sha.matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgY")).isFalse();
|
||||
assertThat(getEncoder().matches("boabspasswurd", "{SSHA}25ro4PKC8jhQZ26jVsozhX/xaP0suHgY")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctPrefixCaseIsUsed() {
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{SSHA}"));
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{ssha}"));
|
||||
this.sha = new LdapShaPasswordEncoder(KeyGenerators.shared(0));
|
||||
this.sha.setForceLowerCasePrefix(false);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{SHA}"));
|
||||
this.sha.setForceLowerCasePrefix(true);
|
||||
assertThat(this.sha.encode("somepassword").startsWith("{SSHA}"));
|
||||
LdapShaPasswordEncoder ldap = getEncoder();
|
||||
ldap.setForceLowerCasePrefix(false);
|
||||
assertThat(ldap.encode("somepassword").startsWith("{SSHA}"));
|
||||
ldap.setForceLowerCasePrefix(true);
|
||||
assertThat(ldap.encode("somepassword").startsWith("{ssha}"));
|
||||
setEncoder(new LdapShaPasswordEncoder(KeyGenerators.shared(0)));
|
||||
ldap.setForceLowerCasePrefix(false);
|
||||
assertThat(getEncoder().encode("somepassword").startsWith("{SHA}"));
|
||||
ldap.setForceLowerCasePrefix(true);
|
||||
assertThat(getEncoder().encode("somepassword").startsWith("{SSHA}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidPrefixIsRejected() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.sha.matches("somepassword", "{MD9}xxxxxxxxxx"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> getEncoder().matches("somepassword", "{MD9}xxxxxxxxxx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void malformedPrefixIsRejected() {
|
||||
// No right brace
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.sha.matches("somepassword", "{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX"));
|
||||
.isThrownBy(() -> getEncoder().matches("somepassword", "{SSHA25ro4PKC8jhQZ26jVsozhX/xaP0suHgX"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
-24
@@ -16,59 +16,58 @@
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Md4PasswordEncoderTests {
|
||||
public class Md4PasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(new Md4PasswordEncoder());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenEncodedPasswordNullThenFalse() {
|
||||
assertThat(getEncoder().matches("raw", null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenEncodedPasswordEmptyThenFalse() {
|
||||
assertThat(getEncoder().matches("raw", "")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeUnsaltedPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
Md4PasswordEncoder md4 = getEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches("ww_uni123", "8zobtq72iAt0W6KNqavGwg==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSaltedPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
Md4PasswordEncoder md4 = getEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches("ww_uni123", "{Alan K Stewart}ZplT6P5Kv6Rlu6W4FIoYNA==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeNullPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches(null, "MdbP4NFq6TG3PFnX4MCJwA==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeEmptyPassword() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
md4.setEncodeHashAsBase64(true);
|
||||
assertThat(md4.matches(null, "MdbP4NFq6TG3PFnX4MCJwA==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonAsciiPasswordHasCorrectHash() {
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
assertThat(md4.matches("\u4F60\u597d", "a7f1196539fd1f85f754ffd185b16e6e")).isTrue();
|
||||
assertThat(getEncoder().matches("\u4F60\u597d", "a7f1196539fd1f85f754ffd185b16e6e")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodedMatches() {
|
||||
String rawPassword = "password";
|
||||
Md4PasswordEncoder md4 = new Md4PasswordEncoder();
|
||||
String encodedPassword = md4.encode(rawPassword);
|
||||
assertThat(md4.matches(rawPassword, encodedPassword)).isTrue();
|
||||
String encodedPassword = getEncoder().encode(rawPassword);
|
||||
assertThat(getEncoder().matches(rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javadocWhenHasSaltThenMatches() {
|
||||
Md4PasswordEncoder encoder = new Md4PasswordEncoder();
|
||||
assertThat(encoder.matches("password", "{thisissalt}6cc7924dad12ade79dfb99e424f25260"));
|
||||
assertThat(getEncoder().matches("password", "{thisissalt}6cc7924dad12ade79dfb99e424f25260"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
-22
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -32,70 +33,69 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MessageDigestPasswordEncoderTests {
|
||||
public class MessageDigestPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(new MessageDigestPasswordEncoder("MD5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5BasicFunctionality() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
String raw = "abc123";
|
||||
assertThat(pe.matches(raw, "{THIS_IS_A_SALT}a68aafd90299d0b137de28fb4bb68573")).isTrue();
|
||||
assertThat(getEncoder().matches(raw, "{THIS_IS_A_SALT}a68aafd90299d0b137de28fb4bb68573")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5NonAsciiPasswordHasCorrectHash() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
// $ echo -n "??" | md5
|
||||
// 7eca689f0d3389d9dea66ae112e5cfd7
|
||||
assertThat(pe.matches("\u4F60\u597d", "7eca689f0d3389d9dea66ae112e5cfd7")).isTrue();
|
||||
assertThat(getEncoder().matches("\u4F60\u597d", "7eca689f0d3389d9dea66ae112e5cfd7")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5Base64() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
MessageDigestPasswordEncoder pe = getEncoder();
|
||||
pe.setEncodeHashAsBase64(true);
|
||||
assertThat(pe.matches("abc123", "{THIS_IS_A_SALT}poqv2QKZ0LE33ij7S7aFcw==")).isTrue();
|
||||
assertThat(getEncoder().matches("abc123", "{THIS_IS_A_SALT}poqv2QKZ0LE33ij7S7aFcw==")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5StretchFactorIsProcessedCorrectly() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
MessageDigestPasswordEncoder pe = getEncoder();
|
||||
pe.setIterations(2);
|
||||
// Calculate value using:
|
||||
// echo -n password{salt} | openssl md5 -binary | openssl md5
|
||||
assertThat(pe.matches("password", "{salt}eb753fb0c370582b4ee01b30f304b9fc")).isTrue();
|
||||
assertThat(getEncoder().matches("password", "{salt}eb753fb0c370582b4ee01b30f304b9fc")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5MatchesWhenNullSalt() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
assertThat(pe.matches("password", "5f4dcc3b5aa765d61d8327deb882cf99")).isTrue();
|
||||
assertThat(getEncoder().matches("password", "5f4dcc3b5aa765d61d8327deb882cf99")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5MatchesWhenEmptySalt() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
assertThat(pe.matches("password", "{}f1026a66095fc2058c1f8771ed05d6da")).isTrue();
|
||||
assertThat(getEncoder().matches("password", "{}f1026a66095fc2058c1f8771ed05d6da")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5MatchesWhenHasSalt() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
assertThat(pe.matches("password", "{salt}ce421738b1c5540836bdc8ff707f1572")).isTrue();
|
||||
assertThat(getEncoder().matches("password", "{salt}ce421738b1c5540836bdc8ff707f1572")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void md5EncodeThenMatches() {
|
||||
String rawPassword = "password";
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("MD5");
|
||||
String encode = pe.encode(rawPassword);
|
||||
assertThat(pe.matches(rawPassword, encode)).isTrue();
|
||||
String encode = getEncoder().encode(rawPassword);
|
||||
assertThat(getEncoder().matches(rawPassword, encode)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicFunctionality() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("SHA-1");
|
||||
setEncoder(new MessageDigestPasswordEncoder("SHA-1"));
|
||||
String raw = "abc123";
|
||||
assertThat(pe.matches(raw, "{THIS_IS_A_SALT}b2f50ffcbd3407fe9415c062d55f54731f340d32"));
|
||||
assertThat(getEncoder().matches(raw, "{THIS_IS_A_SALT}b2f50ffcbd3407fe9415c062d55f54731f340d32"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,14 +103,15 @@ public class MessageDigestPasswordEncoderTests {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("SHA-1");
|
||||
pe.setEncodeHashAsBase64(true);
|
||||
String raw = "abc123";
|
||||
assertThat(pe.matches(raw, "{THIS_IS_A_SALT}b2f50ffcbd3407fe9415c062d55f54731f340d32"));
|
||||
assertThat(getEncoder().matches(raw, "{THIS_IS_A_SALT}b2f50ffcbd3407fe9415c062d55f54731f340d32"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test256() {
|
||||
MessageDigestPasswordEncoder pe = new MessageDigestPasswordEncoder("SHA-1");
|
||||
String raw = "abc123";
|
||||
assertThat(pe.matches(raw, "{THIS_IS_A_SALT}4b79b7de23eb23b78cc5ede227d532b8a51f89b2ec166f808af76b0dbedc47d7"));
|
||||
assertThat(getEncoder().matches(raw,
|
||||
"{THIS_IS_A_SALT}4b79b7de23eb23b78cc5ede227d532b8a51f89b2ec166f808af76b0dbedc47d7"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.crypto.password;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
/**
|
||||
* Test {@link NoOpPasswordEncoder}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class NoOpPasswordEncoderTests extends AbstractPasswordEncoderValidationTests {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
setEncoder(NoOpPasswordEncoder.getInstance());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user