Polish spring-security-crypto main code
Manually polish `spring-security-crypto` following the formatting and checkstyle fixes. Issue gh-8945
This commit is contained in:
parent
771ef0dadc
commit
da22e97147
|
@ -58,7 +58,6 @@ final class Argon2EncodingUtils {
|
|||
*/
|
||||
static String encode(byte[] hash, Argon2Parameters parameters) throws IllegalArgumentException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
switch (parameters.getType()) {
|
||||
case Argon2Parameters.ARGON2_d:
|
||||
stringBuilder.append("$argon2d");
|
||||
|
@ -74,13 +73,10 @@ final class Argon2EncodingUtils {
|
|||
}
|
||||
stringBuilder.append("$v=").append(parameters.getVersion()).append("$m=").append(parameters.getMemory())
|
||||
.append(",t=").append(parameters.getIterations()).append(",p=").append(parameters.getLanes());
|
||||
|
||||
if (parameters.getSalt() != null) {
|
||||
stringBuilder.append("$").append(b64encoder.encodeToString(parameters.getSalt()));
|
||||
}
|
||||
|
||||
stringBuilder.append("$").append(b64encoder.encodeToString(hash));
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
|
@ -106,15 +102,11 @@ final class Argon2EncodingUtils {
|
|||
*/
|
||||
static Argon2Hash decode(String encodedHash) throws IllegalArgumentException {
|
||||
Argon2Parameters.Builder paramsBuilder;
|
||||
|
||||
String[] parts = encodedHash.split("\\$");
|
||||
|
||||
if (parts.length < 4) {
|
||||
throw new IllegalArgumentException("Invalid encoded Argon2-hash");
|
||||
}
|
||||
|
||||
int currentPart = 1;
|
||||
|
||||
switch (parts[currentPart++]) {
|
||||
case "argon2d":
|
||||
paramsBuilder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_d);
|
||||
|
@ -128,41 +120,27 @@ final class Argon2EncodingUtils {
|
|||
default:
|
||||
throw new IllegalArgumentException("Invalid algorithm type: " + parts[0]);
|
||||
}
|
||||
|
||||
if (parts[currentPart].startsWith("v=")) {
|
||||
paramsBuilder.withVersion(Integer.parseInt(parts[currentPart].substring(2)));
|
||||
currentPart++;
|
||||
}
|
||||
|
||||
String[] performanceParams = parts[currentPart++].split(",");
|
||||
|
||||
if (performanceParams.length != 3) {
|
||||
throw new IllegalArgumentException("Amount of performance parameters invalid");
|
||||
}
|
||||
|
||||
if (performanceParams[0].startsWith("m=")) {
|
||||
paramsBuilder.withMemoryAsKB(Integer.parseInt(performanceParams[0].substring(2)));
|
||||
}
|
||||
else {
|
||||
if (!performanceParams[0].startsWith("m=")) {
|
||||
throw new IllegalArgumentException("Invalid memory parameter");
|
||||
}
|
||||
|
||||
if (performanceParams[1].startsWith("t=")) {
|
||||
paramsBuilder.withIterations(Integer.parseInt(performanceParams[1].substring(2)));
|
||||
}
|
||||
else {
|
||||
paramsBuilder.withMemoryAsKB(Integer.parseInt(performanceParams[0].substring(2)));
|
||||
if (!performanceParams[1].startsWith("t=")) {
|
||||
throw new IllegalArgumentException("Invalid iterations parameter");
|
||||
}
|
||||
|
||||
if (performanceParams[2].startsWith("p=")) {
|
||||
paramsBuilder.withParallelism(Integer.parseInt(performanceParams[2].substring(2)));
|
||||
}
|
||||
else {
|
||||
paramsBuilder.withIterations(Integer.parseInt(performanceParams[1].substring(2)));
|
||||
if (!performanceParams[2].startsWith("p=")) {
|
||||
throw new IllegalArgumentException("Invalid parallelity parameter");
|
||||
}
|
||||
|
||||
paramsBuilder.withParallelism(Integer.parseInt(performanceParams[2].substring(2)));
|
||||
paramsBuilder.withSalt(b64decoder.decode(parts[currentPart++]));
|
||||
|
||||
return new Argon2Hash(b64decoder.decode(parts[currentPart]), paramsBuilder.build());
|
||||
}
|
||||
|
||||
|
|
|
@ -68,30 +68,27 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
|||
|
||||
private final BytesKeyGenerator saltGenerator;
|
||||
|
||||
public Argon2PasswordEncoder() {
|
||||
this(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY, DEFAULT_ITERATIONS);
|
||||
}
|
||||
|
||||
public Argon2PasswordEncoder(int saltLength, int hashLength, int parallelism, int memory, int iterations) {
|
||||
this.hashLength = hashLength;
|
||||
this.parallelism = parallelism;
|
||||
this.memory = memory;
|
||||
this.iterations = iterations;
|
||||
|
||||
this.saltGenerator = KeyGenerators.secureRandom(saltLength);
|
||||
}
|
||||
|
||||
public Argon2PasswordEncoder() {
|
||||
this(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY, DEFAULT_ITERATIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
byte[] hash = new byte[this.hashLength];
|
||||
|
||||
Argon2Parameters params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id).withSalt(salt)
|
||||
.withParallelism(this.parallelism).withMemoryAsKB(this.memory).withIterations(this.iterations).build();
|
||||
Argon2BytesGenerator generator = new Argon2BytesGenerator();
|
||||
generator.init(params);
|
||||
generator.generateBytes(rawPassword.toString().toCharArray(), hash);
|
||||
|
||||
return Argon2EncodingUtils.encode(hash, params);
|
||||
}
|
||||
|
||||
|
@ -101,9 +98,7 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
|||
this.logger.warn("password hash is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
Argon2EncodingUtils.Argon2Hash decoded;
|
||||
|
||||
try {
|
||||
decoded = Argon2EncodingUtils.decode(encodedPassword);
|
||||
}
|
||||
|
@ -111,13 +106,10 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
|||
this.logger.warn("Malformed password hash", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] hashBytes = new byte[decoded.getHash().length];
|
||||
|
||||
Argon2BytesGenerator generator = new Argon2BytesGenerator();
|
||||
generator.init(decoded.getParameters());
|
||||
generator.generateBytes(rawPassword.toString().toCharArray(), hashBytes);
|
||||
|
||||
return constantTimeArrayEquals(decoded.getHash(), hashBytes);
|
||||
}
|
||||
|
||||
|
@ -127,9 +119,7 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
|||
this.logger.warn("password hash is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
Argon2Parameters parameters = Argon2EncodingUtils.decode(encodedPassword).getParameters();
|
||||
|
||||
return parameters.getMemory() < this.memory || parameters.getIterations() < this.iterations;
|
||||
}
|
||||
|
||||
|
@ -137,7 +127,6 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
|
|||
if (expected.length != actual.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
result |= expected[i] ^ actual[i];
|
||||
|
|
|
@ -106,33 +106,30 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
|||
if (rawPassword == null) {
|
||||
throw new IllegalArgumentException("rawPassword cannot be null");
|
||||
}
|
||||
|
||||
String salt;
|
||||
if (this.random != null) {
|
||||
salt = BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
|
||||
}
|
||||
else {
|
||||
salt = BCrypt.gensalt(this.version.getVersion(), this.strength);
|
||||
}
|
||||
String salt = getSalt();
|
||||
return BCrypt.hashpw(rawPassword.toString(), salt);
|
||||
}
|
||||
|
||||
private String getSalt() {
|
||||
if (this.random != null) {
|
||||
return BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
|
||||
}
|
||||
return BCrypt.gensalt(this.version.getVersion(), this.strength);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
|
||||
this.logger.warn("Encoded password does not look like BCrypt");
|
||||
return false;
|
||||
}
|
||||
|
||||
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
|
||||
}
|
||||
|
||||
|
@ -142,15 +139,12 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
|||
this.logger.warn("Empty encoded password");
|
||||
return false;
|
||||
}
|
||||
|
||||
Matcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Encoded password does not look like BCrypt: " + encodedPassword);
|
||||
}
|
||||
else {
|
||||
int strength = Integer.parseInt(matcher.group(2));
|
||||
return strength < this.strength;
|
||||
}
|
||||
int strength = Integer.parseInt(matcher.group(2));
|
||||
return strength < this.strength;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +154,11 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
|
|||
*/
|
||||
public enum BCryptVersion {
|
||||
|
||||
$2A("$2a"), $2Y("$2y"), $2B("$2b");
|
||||
$2A("$2a"),
|
||||
|
||||
$2Y("$2y"),
|
||||
|
||||
$2B("$2b");
|
||||
|
||||
private final String version;
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ package org.springframework.security.crypto.codec;
|
|||
*/
|
||||
public final class Hex {
|
||||
|
||||
private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
|
||||
'f' };
|
||||
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
||||
|
||||
private Hex() {
|
||||
}
|
||||
|
@ -36,7 +35,6 @@ public final class Hex {
|
|||
public static char[] encode(byte[] bytes) {
|
||||
final int nBytes = bytes.length;
|
||||
char[] result = new char[2 * nBytes];
|
||||
|
||||
int j = 0;
|
||||
for (byte aByte : bytes) {
|
||||
// Char for top 4 bits
|
||||
|
@ -44,23 +42,18 @@ public final class Hex {
|
|||
// Bottom 4
|
||||
result[j++] = HEX[(0x0F & aByte)];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] decode(CharSequence s) {
|
||||
int nChars = s.length();
|
||||
|
||||
if (nChars % 2 != 0) {
|
||||
throw new IllegalArgumentException("Hex-encoded string must have an even number of characters");
|
||||
}
|
||||
|
||||
byte[] result = new byte[nChars / 2];
|
||||
|
||||
for (int i = 0; i < nChars; i += 2) {
|
||||
int msb = Character.digit(s.charAt(i), 16);
|
||||
int lsb = Character.digit(s.charAt(i + 1), 16);
|
||||
|
||||
if (msb < 0 || lsb < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
|
||||
|
|
|
@ -44,7 +44,6 @@ public final class Utf8 {
|
|||
ByteBuffer bytes = CHARSET.newEncoder().encode(CharBuffer.wrap(string));
|
||||
byte[] bytesCopy = new byte[bytes.limit()];
|
||||
System.arraycopy(bytes.array(), 0, bytesCopy, 0, bytes.limit());
|
||||
|
||||
return bytesCopy;
|
||||
}
|
||||
catch (CharacterCodingException ex) {
|
||||
|
|
|
@ -52,38 +52,6 @@ public final class AesBytesEncryptor implements BytesEncryptor {
|
|||
|
||||
private static final String AES_GCM_ALGORITHM = "AES/GCM/NoPadding";
|
||||
|
||||
public enum CipherAlgorithm {
|
||||
|
||||
CBC(AES_CBC_ALGORITHM, NULL_IV_GENERATOR), GCM(AES_GCM_ALGORITHM, KeyGenerators.secureRandom(16));
|
||||
|
||||
private BytesKeyGenerator ivGenerator;
|
||||
|
||||
private String name;
|
||||
|
||||
CipherAlgorithm(String name, BytesKeyGenerator ivGenerator) {
|
||||
this.name = name;
|
||||
this.ivGenerator = ivGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public AlgorithmParameterSpec getParameterSpec(byte[] iv) {
|
||||
return (this != CBC) ? new GCMParameterSpec(128, iv) : new IvParameterSpec(iv);
|
||||
}
|
||||
|
||||
public Cipher createCipher() {
|
||||
return CipherUtils.newCipher(this.toString());
|
||||
}
|
||||
|
||||
public BytesKeyGenerator defaultIvGenerator() {
|
||||
return this.ivGenerator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AesBytesEncryptor(String password, CharSequence salt) {
|
||||
this(password, salt, null);
|
||||
}
|
||||
|
@ -159,4 +127,38 @@ public final class AesBytesEncryptor implements BytesEncryptor {
|
|||
|
||||
};
|
||||
|
||||
public enum CipherAlgorithm {
|
||||
|
||||
CBC(AES_CBC_ALGORITHM, NULL_IV_GENERATOR),
|
||||
|
||||
GCM(AES_GCM_ALGORITHM, KeyGenerators.secureRandom(16));
|
||||
|
||||
private BytesKeyGenerator ivGenerator;
|
||||
|
||||
private String name;
|
||||
|
||||
CipherAlgorithm(String name, BytesKeyGenerator ivGenerator) {
|
||||
this.name = name;
|
||||
this.ivGenerator = ivGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public AlgorithmParameterSpec getParameterSpec(byte[] iv) {
|
||||
return (this != CBC) ? new GCMParameterSpec(128, iv) : new IvParameterSpec(iv);
|
||||
}
|
||||
|
||||
public Cipher createCipher() {
|
||||
return CipherUtils.newCipher(this.toString());
|
||||
}
|
||||
|
||||
public BytesKeyGenerator defaultIvGenerator() {
|
||||
return this.ivGenerator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.springframework.security.crypto.util.EncodingUtils;
|
|||
* "AES/CBC/PKCS5Padding".
|
||||
*
|
||||
* @author William Tran
|
||||
*
|
||||
*/
|
||||
public class BouncyCastleAesCbcBytesEncryptor extends BouncyCastleAesBytesEncryptor {
|
||||
|
||||
|
@ -46,10 +45,9 @@ public class BouncyCastleAesCbcBytesEncryptor extends BouncyCastleAesBytesEncryp
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public byte[] encrypt(byte[] bytes) {
|
||||
byte[] iv = this.ivGenerator.generateKey();
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(
|
||||
new CBCBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine()), new PKCS7Padding());
|
||||
blockCipher.init(true, new ParametersWithIV(this.secretKey, iv));
|
||||
|
@ -58,11 +56,10 @@ public class BouncyCastleAesCbcBytesEncryptor extends BouncyCastleAesBytesEncryp
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public byte[] decrypt(byte[] encryptedBytes) {
|
||||
byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength());
|
||||
encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(
|
||||
new CBCBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine()), new PKCS7Padding());
|
||||
blockCipher.init(false, new ParametersWithIV(this.secretKey, iv));
|
||||
|
|
|
@ -44,23 +44,20 @@ public class BouncyCastleAesGcmBytesEncryptor extends BouncyCastleAesBytesEncryp
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public byte[] encrypt(byte[] bytes) {
|
||||
byte[] iv = this.ivGenerator.generateKey();
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
GCMBlockCipher blockCipher = new GCMBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine());
|
||||
blockCipher.init(true, new AEADParameters(this.secretKey, 128, iv, null));
|
||||
|
||||
byte[] encrypted = process(blockCipher, bytes);
|
||||
return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public byte[] decrypt(byte[] encryptedBytes) {
|
||||
byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength());
|
||||
encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
GCMBlockCipher blockCipher = new GCMBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine());
|
||||
blockCipher.init(false, new AEADParameters(this.secretKey, 128, iv, null));
|
||||
return process(blockCipher, encryptedBytes);
|
||||
|
|
|
@ -113,13 +113,13 @@ public final class Encryptors {
|
|||
* environments where working with plain text strings is desired for simplicity.
|
||||
*/
|
||||
public static TextEncryptor noOpText() {
|
||||
return NO_OP_TEXT_INSTANCE;
|
||||
return NoOpTextEncryptor.INSTANCE;
|
||||
}
|
||||
|
||||
private static final TextEncryptor NO_OP_TEXT_INSTANCE = new NoOpTextEncryptor();
|
||||
|
||||
private static final class NoOpTextEncryptor implements TextEncryptor {
|
||||
|
||||
static final TextEncryptor INSTANCE = new NoOpTextEncryptor();
|
||||
|
||||
@Override
|
||||
public String encrypt(String text) {
|
||||
return text;
|
||||
|
|
|
@ -78,7 +78,6 @@ public final class PasswordEncoderFactories {
|
|||
new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
|
||||
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
|
||||
encoders.put("argon2", new Argon2PasswordEncoder());
|
||||
|
||||
return new DelegatingPasswordEncoder(encodingId, encoders);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.security.SecureRandom;
|
|||
*/
|
||||
final class SecureRandomBytesKeyGenerator implements BytesKeyGenerator {
|
||||
|
||||
private static final int DEFAULT_KEY_LENGTH = 8;
|
||||
|
||||
private final SecureRandom random;
|
||||
|
||||
private final int keyLength;
|
||||
|
@ -59,6 +61,4 @@ final class SecureRandomBytesKeyGenerator implements BytesKeyGenerator {
|
|||
return bytes;
|
||||
}
|
||||
|
||||
private static final int DEFAULT_KEY_LENGTH = 8;
|
||||
|
||||
}
|
||||
|
|
|
@ -75,11 +75,9 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
|||
if (salt == null) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
byte[] hashAndSalt = new byte[hash.length + salt.length];
|
||||
System.arraycopy(hash, 0, hashAndSalt, 0, hash.length);
|
||||
System.arraycopy(salt, 0, hashAndSalt, hash.length, salt.length);
|
||||
|
||||
return hashAndSalt;
|
||||
}
|
||||
|
||||
|
@ -98,42 +96,39 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
|||
}
|
||||
|
||||
private String encode(CharSequence rawPassword, byte[] salt) {
|
||||
MessageDigest sha;
|
||||
MessageDigest sha = getSha(rawPassword);
|
||||
if (salt != null) {
|
||||
sha.update(salt);
|
||||
}
|
||||
byte[] hash = combineHashAndSalt(sha.digest(), salt);
|
||||
String prefix = getPrefix(salt);
|
||||
return prefix + Utf8.decode(Base64.getEncoder().encode(hash));
|
||||
}
|
||||
|
||||
private MessageDigest getSha(CharSequence rawPassword) {
|
||||
try {
|
||||
sha = MessageDigest.getInstance("SHA");
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA");
|
||||
sha.update(Utf8.encode(rawPassword));
|
||||
return sha;
|
||||
}
|
||||
catch (java.security.NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException("No SHA implementation available!");
|
||||
}
|
||||
}
|
||||
|
||||
if (salt != null) {
|
||||
sha.update(salt);
|
||||
}
|
||||
|
||||
byte[] hash = combineHashAndSalt(sha.digest(), salt);
|
||||
|
||||
String prefix;
|
||||
|
||||
private String getPrefix(byte[] salt) {
|
||||
if (salt == null || salt.length == 0) {
|
||||
prefix = this.forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
|
||||
return this.forceLowerCasePrefix ? SHA_PREFIX_LC : SHA_PREFIX;
|
||||
}
|
||||
else {
|
||||
prefix = this.forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
|
||||
}
|
||||
|
||||
return prefix + Utf8.decode(Base64.getEncoder().encode(hash));
|
||||
return this.forceLowerCasePrefix ? SSHA_PREFIX_LC : SSHA_PREFIX;
|
||||
}
|
||||
|
||||
private byte[] extractSalt(String encPass) {
|
||||
String encPassNoLabel = encPass.substring(6);
|
||||
|
||||
byte[] hashAndSalt = Base64.getDecoder().decode(encPassNoLabel.getBytes());
|
||||
int saltLength = hashAndSalt.length - SHA_LENGTH;
|
||||
byte[] salt = new byte[saltLength];
|
||||
System.arraycopy(hashAndSalt, SHA_LENGTH, salt, 0, saltLength);
|
||||
|
||||
return salt;
|
||||
}
|
||||
|
||||
|
@ -151,28 +146,24 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
|||
|
||||
private boolean matches(String rawPassword, String encodedPassword) {
|
||||
String prefix = extractPrefix(encodedPassword);
|
||||
|
||||
if (prefix == null) {
|
||||
return PasswordEncoderUtils.equals(encodedPassword, rawPassword);
|
||||
}
|
||||
byte[] salt = getSalt(encodedPassword, prefix);
|
||||
int startOfHash = prefix.length();
|
||||
String encodedRawPass = encode(rawPassword, salt).substring(startOfHash);
|
||||
return PasswordEncoderUtils.equals(encodedRawPass, encodedPassword.substring(startOfHash));
|
||||
}
|
||||
|
||||
byte[] salt;
|
||||
private byte[] getSalt(String encodedPassword, String prefix) {
|
||||
if (prefix.equals(SSHA_PREFIX) || prefix.equals(SSHA_PREFIX_LC)) {
|
||||
salt = extractSalt(encodedPassword);
|
||||
return extractSalt(encodedPassword);
|
||||
}
|
||||
else if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
|
||||
if (!prefix.equals(SHA_PREFIX) && !prefix.equals(SHA_PREFIX_LC)) {
|
||||
throw new IllegalArgumentException("Unsupported password prefix '" + prefix + "'");
|
||||
}
|
||||
else {
|
||||
// Standard SHA
|
||||
salt = null;
|
||||
}
|
||||
|
||||
int startOfHash = prefix.length();
|
||||
|
||||
String encodedRawPass = encode(rawPassword, salt).substring(startOfHash);
|
||||
|
||||
return PasswordEncoderUtils.equals(encodedRawPass, encodedPassword.substring(startOfHash));
|
||||
// Standard SHA
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -182,13 +173,10 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
|
|||
if (!encPass.startsWith("{")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int secondBrace = encPass.lastIndexOf('}');
|
||||
|
||||
if (secondBrace < 0) {
|
||||
throw new IllegalArgumentException("Couldn't find closing brace for SHA prefix");
|
||||
}
|
||||
|
||||
return encPass.substring(0, secondBrace + 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,16 +76,13 @@ class Md4 {
|
|||
update(this.buffer, 0);
|
||||
this.bufferOffset = 0;
|
||||
}
|
||||
|
||||
while (this.bufferOffset < C) {
|
||||
this.buffer[this.bufferOffset++] = (byte) 0x00;
|
||||
}
|
||||
|
||||
long bitCount = this.byteCount * 8;
|
||||
for (int i = 0; i < 64; i += 8) {
|
||||
this.buffer[this.bufferOffset++] = (byte) (bitCount >>> (i));
|
||||
}
|
||||
|
||||
update(this.buffer, 0);
|
||||
digest(buffer, offset);
|
||||
}
|
||||
|
|
|
@ -111,10 +111,8 @@ public class Md4PasswordEncoder implements PasswordEncoder {
|
|||
}
|
||||
String saltedPassword = rawPassword + salt;
|
||||
byte[] saltedPasswordBytes = Utf8.encode(saltedPassword);
|
||||
|
||||
Md4 md4 = new Md4();
|
||||
md4.update(saltedPasswordBytes, 0, saltedPasswordBytes.length);
|
||||
|
||||
byte[] digest = md4.digest();
|
||||
String encoded = encode(digest);
|
||||
return salt + encoded;
|
||||
|
@ -124,9 +122,7 @@ public class Md4PasswordEncoder implements PasswordEncoder {
|
|||
if (this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.getEncoder().encode(digest));
|
||||
}
|
||||
else {
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -123,7 +123,6 @@ 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);
|
||||
return salt + encoded;
|
||||
|
@ -133,9 +132,7 @@ public class MessageDigestPasswordEncoder implements PasswordEncoder {
|
|||
if (this.encodeHashAsBase64) {
|
||||
return Utf8.decode(Base64.getEncoder().encode(digest));
|
||||
}
|
||||
else {
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
return new String(Hex.encode(digest));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,11 @@ package org.springframework.security.crypto.password;
|
|||
@Deprecated
|
||||
public final class NoOpPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
private static final PasswordEncoder INSTANCE = new NoOpPasswordEncoder();
|
||||
|
||||
private NoOpPasswordEncoder() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return rawPassword.toString();
|
||||
|
@ -50,9 +55,4 @@ public final class NoOpPasswordEncoder implements PasswordEncoder {
|
|||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final PasswordEncoder INSTANCE = new NoOpPasswordEncoder();
|
||||
|
||||
private NoOpPasswordEncoder() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,17 +39,13 @@ final class PasswordEncoderUtils {
|
|||
static boolean equals(String expected, String actual) {
|
||||
byte[] expectedBytes = bytesUtf8(expected);
|
||||
byte[] actualBytes = bytesUtf8(actual);
|
||||
|
||||
return MessageDigest.isEqual(expectedBytes, actualBytes);
|
||||
}
|
||||
|
||||
private static byte[] bytesUtf8(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
// need to check if Utf8.encode() runs in constant time (probably not).
|
||||
// This may leak length of string.
|
||||
return Utf8.encode(s);
|
||||
return (s != null) ? Utf8.encode(s) : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,11 +112,11 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
|||
String algorithmName = secretKeyFactoryAlgorithm.name();
|
||||
try {
|
||||
SecretKeyFactory.getInstance(algorithmName);
|
||||
this.algorithm = algorithmName;
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalArgumentException("Invalid algorithm '" + algorithmName + "'.", ex);
|
||||
}
|
||||
this.algorithm = algorithmName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -50,6 +50,8 @@ import org.springframework.security.crypto.util.EncodingUtils;
|
|||
@Deprecated
|
||||
public final class StandardPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
private static final int DEFAULT_ITERATIONS = 1024;
|
||||
|
||||
private final Digester digester;
|
||||
|
||||
private final byte[] secret;
|
||||
|
@ -104,6 +106,4 @@ public final class StandardPasswordEncoder implements PasswordEncoder {
|
|||
return Hex.decode(encodedPassword);
|
||||
}
|
||||
|
||||
private static final int DEFAULT_ITERATIONS = 1024;
|
||||
|
||||
}
|
||||
|
|
|
@ -109,7 +109,6 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
|||
if (saltLength < 1 || saltLength > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Salt length must be >= 1 and <= " + Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
this.cpuCost = cpuCost;
|
||||
this.memoryCost = memoryCost;
|
||||
this.parallelization = parallelization;
|
||||
|
@ -136,56 +135,43 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
|||
if (encodedPassword == null || encodedPassword.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] parts = encodedPassword.split("\\$");
|
||||
|
||||
if (parts.length != 4) {
|
||||
throw new IllegalArgumentException("Encoded password does not look like SCrypt: " + encodedPassword);
|
||||
}
|
||||
|
||||
long params = Long.parseLong(parts[1], 16);
|
||||
|
||||
int cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);
|
||||
int memoryCost = (int) params >> 8 & 0xff;
|
||||
int parallelization = (int) params & 0xff;
|
||||
|
||||
return cpuCost < this.cpuCost || memoryCost < this.memoryCost || parallelization < this.parallelization;
|
||||
}
|
||||
|
||||
private boolean decodeAndCheckMatches(CharSequence rawPassword, String encodedPassword) {
|
||||
String[] parts = encodedPassword.split("\\$");
|
||||
|
||||
if (parts.length != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long params = Long.parseLong(parts[1], 16);
|
||||
byte[] salt = decodePart(parts[2]);
|
||||
byte[] derived = decodePart(parts[3]);
|
||||
|
||||
int cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);
|
||||
int memoryCost = (int) params >> 8 & 0xff;
|
||||
int parallelization = (int) params & 0xff;
|
||||
|
||||
byte[] generated = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization,
|
||||
this.keyLength);
|
||||
|
||||
return MessageDigest.isEqual(derived, generated);
|
||||
}
|
||||
|
||||
private String digest(CharSequence rawPassword, byte[] salt) {
|
||||
byte[] derived = SCrypt.generate(Utf8.encode(rawPassword), salt, this.cpuCost, this.memoryCost,
|
||||
this.parallelization, this.keyLength);
|
||||
|
||||
String params = Long.toString(
|
||||
((int) (Math.log(this.cpuCost) / Math.log(2)) << 16L) | this.memoryCost << 8 | this.parallelization,
|
||||
16);
|
||||
|
||||
StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);
|
||||
sb.append("$").append(params).append('$');
|
||||
sb.append(encodePart(salt)).append('$');
|
||||
sb.append(encodePart(derived));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue