Pbkdf2PasswordEncoder supports Base64 encoding
Fixes gh-4683
This commit is contained in:
parent
fe8f3afbaf
commit
870b8bf9b2
|
@ -17,6 +17,7 @@ package org.springframework.security.crypto.password;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
import javax.crypto.SecretKeyFactory;
|
import javax.crypto.SecretKeyFactory;
|
||||||
import javax.crypto.spec.PBEKeySpec;
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
@ -52,6 +53,7 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||||
private final int hashWidth;
|
private final int hashWidth;
|
||||||
private final int iterations;
|
private final int iterations;
|
||||||
private String algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name();
|
private String algorithm = SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name();
|
||||||
|
private boolean encodeHashAsBase64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a PBKDF2 password encoder with no additional secret value. There will be
|
* Constructs a PBKDF2 password encoder with no additional secret value. There will be
|
||||||
|
@ -110,16 +112,33 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||||
this.algorithm = algorithmName;
|
this.algorithm = algorithmName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if the resulting hash should be encoded as Base64. The default is false which
|
||||||
|
* means it will be encoded in Hex.
|
||||||
|
* @param encodeHashAsBase64 true if encode as Base64, false if should use Hex
|
||||||
|
* (default)
|
||||||
|
*/
|
||||||
|
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
|
||||||
|
this.encodeHashAsBase64 = encodeHashAsBase64;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encode(CharSequence rawPassword) {
|
public String encode(CharSequence rawPassword) {
|
||||||
byte[] salt = this.saltGenerator.generateKey();
|
byte[] salt = this.saltGenerator.generateKey();
|
||||||
byte[] encoded = encode(rawPassword, salt);
|
byte[] encoded = encode(rawPassword, salt);
|
||||||
return String.valueOf(Hex.encode(encoded));
|
return encode(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encode(byte[] bytes) {
|
||||||
|
if(this.encodeHashAsBase64) {
|
||||||
|
return Base64.getEncoder().encodeToString(bytes);
|
||||||
|
}
|
||||||
|
return String.valueOf(Hex.encode(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||||
byte[] digested = Hex.decode(encodedPassword);
|
byte[] digested = decode(encodedPassword);
|
||||||
byte[] salt = subArray(digested, 0, this.saltGenerator.getKeyLength());
|
byte[] salt = subArray(digested, 0, this.saltGenerator.getKeyLength());
|
||||||
return matches(digested, encode(rawPassword, salt));
|
return matches(digested, encode(rawPassword, salt));
|
||||||
}
|
}
|
||||||
|
@ -139,6 +158,13 @@ public class Pbkdf2PasswordEncoder implements PasswordEncoder {
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] decode(String encodedBytes) {
|
||||||
|
if(this.encodeHashAsBase64) {
|
||||||
|
return Base64.getDecoder().decode(encodedBytes);
|
||||||
|
}
|
||||||
|
return Hex.decode(encodedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] encode(CharSequence rawPassword, byte[] salt) {
|
private byte[] encode(CharSequence rawPassword, byte[] salt) {
|
||||||
try {
|
try {
|
||||||
PBEKeySpec spec = new PBEKeySpec(rawPassword.toString().toCharArray(),
|
PBEKeySpec spec = new PBEKeySpec(rawPassword.toString().toCharArray(),
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Arrays;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.crypto.codec.Base64;
|
||||||
import org.springframework.security.crypto.codec.Hex;
|
import org.springframework.security.crypto.codec.Hex;
|
||||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||||
|
|
||||||
|
@ -75,6 +76,25 @@ public class Pbkdf2PasswordEncoderTests {
|
||||||
assertThat(fixedHex).isEqualTo(encodedPassword);
|
assertThat(fixedHex).isEqualTo(encodedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encodeAndMatchWhenBase64ThenSuccess() {
|
||||||
|
this.encoder.setEncodeHashAsBase64(true);
|
||||||
|
|
||||||
|
String rawPassword = "password";
|
||||||
|
String encodedPassword = this.encoder.encode(rawPassword);
|
||||||
|
assertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void matchWhenBase64ThenSuccess() {
|
||||||
|
this.encoder.setEncodeHashAsBase64(true);
|
||||||
|
String rawPassword = "password";
|
||||||
|
String encodedPassword = "3FOwOMcDgxP+z1x/sv184LFY2WVD+ZGMgYP3LPOSmCcDmk1XPYvcCQ==";
|
||||||
|
|
||||||
|
assertThat(this.encoder.matches(rawPassword, encodedPassword)).isTrue();
|
||||||
|
java.util.Base64.getDecoder().decode(encodedPassword); // validate can decode as Base64
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void encodeAndMatchWhenSha256ThenSuccess() {
|
public void encodeAndMatchWhenSha256ThenSuccess() {
|
||||||
this.encoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);
|
this.encoder.setAlgorithm(Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);
|
||||||
|
|
Loading…
Reference in New Issue