Pbkdf2PasswordEncoder allows custom SecretKeyFactory
Fixes gh-2742
This commit is contained in:
parent
b91aa19b35
commit
fe8f3afbaf
|
@ -16,6 +16,7 @@
|
|||
package org.springframework.security.crypto.password;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
@ -41,7 +42,7 @@ import static org.springframework.security.crypto.util.EncodingUtils.subArray;
|
|||
* @since 4.1
|
||||
*/
|
||||
public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
|
||||
|
||||
private static final int DEFAULT_HASH_WIDTH = 256;
|
||||
private static final int DEFAULT_ITERATIONS = 185000;
|
||||
|
||||
|
@ -50,6 +51,7 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
|||
private final byte[] secret;
|
||||
private final int hashWidth;
|
||||
private final int iterations;
|
||||
private String algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name();
|
||||
|
||||
/**
|
||||
* Constructs a PBKDF2 password encoder with no additional secret value. There will be
|
||||
|
@ -86,6 +88,28 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
|||
this.hashWidth = hashWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the algorithm to use. See
|
||||
* <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory">SecretKeyFactory Algorithms</a>
|
||||
* @param secretKeyFactoryAlgorithm the algorithm to use (i.e.
|
||||
* {@code Pbkdf2PasswordEncoder.PBKDF2_WITH_HMAC_SHA1},
|
||||
* {@code Pbkdf2PasswordEncoder.PBKDF2_WITH_HMAC_SHA256},
|
||||
* {@code Pbkdf2PasswordEncoder.PBKDF2_WITH_HMAC_SHA512})
|
||||
*/
|
||||
public void setAlgorithm(SecretKeyFactoryAlgorithm secretKeyFactoryAlgorithm) {
|
||||
if(secretKeyFactoryAlgorithm == null) {
|
||||
throw new IllegalArgumentException("secretKeyFactoryAlgorithm cannot be null");
|
||||
}
|
||||
String algorithmName = secretKeyFactoryAlgorithm.name();
|
||||
try {
|
||||
SecretKeyFactory.getInstance(algorithmName);
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException("Invalid algorithm '" + algorithmName + "'.", e);
|
||||
}
|
||||
this.algorithm = algorithmName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
byte[] salt = this.saltGenerator.generateKey();
|
||||
|
@ -119,11 +143,20 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
|||
try {
|
||||
PBEKeySpec spec = new PBEKeySpec(rawPassword.toString().toCharArray(),
|
||||
concatenate(salt, this.secret), this.iterations, this.hashWidth);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(this.algorithm);
|
||||
return concatenate(salt, skf.generateSecret(spec).getEncoded());
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IllegalStateException("Could not create hash", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Algorithm used for creating the {@link SecretKeyFactory}
|
||||
*/
|
||||
public enum SecretKeyFactoryAlgorithm {
|
||||
PBKDF2WithHmacSHA1,
|
||||
PBKDF2WithHmacSHA256,
|
||||
PBKDF2WithHmacSHA512
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,23 @@ public class Pbkdf2PasswordEncoderTests {
|
|||
assertThat(fixedHex).isEqualTo(encodedPassword);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeAndMatchWhenSha256ThenSuccess() {
|
||||
this.encoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);
|
||||
|
||||
String rawPassword = "password";
|
||||
String encodedPassword = this.encoder.encode(rawPassword);
|
||||
assertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchWhenSha256ThenSuccess() {
|
||||
this.encoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);
|
||||
|
||||
String rawPassword = "password";
|
||||
String encodedPassword = "821447f994e2b04c5014e31fa9fca4ae1cc9f2188c4ed53d3ddb5ba7980982b51a0ecebfc0b81a79";
|
||||
assertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();
|
||||
}
|
||||
/**
|
||||
* Used to find the iteration count that takes .5 seconds.
|
||||
*/
|
||||
|
@ -105,4 +122,4 @@ public class Pbkdf2PasswordEncoderTests {
|
|||
}
|
||||
System.out.println("Iterations " + iterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue