Make sure PBKDF2 providers are using the expect size for derived keys (#16798)

Closes #16797
This commit is contained in:
Pedro Igor 2023-02-03 06:31:25 -08:00 committed by GitHub
parent f8f112d8d2
commit d97b9c48c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 3 deletions

View file

@ -35,6 +35,9 @@ import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentValidationException; import org.keycloak.component.ComponentValidationException;
import org.keycloak.credential.hash.Pbkdf2PasswordHashProvider;
import org.keycloak.credential.hash.Pbkdf2Sha256PasswordHashProviderFactory;
import org.keycloak.credential.hash.Pbkdf2Sha512PasswordHashProviderFactory;
import org.keycloak.jose.jws.JWSBuilder; import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.rule.CryptoInitRule; import org.keycloak.rule.CryptoInitRule;
@ -63,8 +66,12 @@ public class CryptoPerfTest {
} }
private void perfTest(Runnable runnable, String testName) { private void perfTest(Runnable runnable, String testName) {
perfTest(runnable, testName, COUNT_ITERATIONS);
}
private void perfTest(Runnable runnable, String testName, int count) {
long start = Time.currentTimeMillis(); long start = Time.currentTimeMillis();
for (int i=0 ; i<COUNT_ITERATIONS ; i++) { for (int i=0 ; i<count ; i++) {
runnable.run(); runnable.run();
} }
long took = Time.currentTimeMillis() - start; long took = Time.currentTimeMillis() - start;
@ -102,6 +109,39 @@ public class CryptoPerfTest {
perfTest(() -> testTokenSignAndVerify(keyPair), "testSignAndVerifyTokens2048"); perfTest(() -> testTokenSignAndVerify(keyPair), "testSignAndVerifyTokens2048");
} }
@Test
public void testPbkdf256() {
int iterations = 600 * 1000;
int derivedKeySize = 256;
String providerId = Pbkdf2Sha256PasswordHashProviderFactory.ID;
String algorithm = Pbkdf2Sha256PasswordHashProviderFactory.PBKDF2_ALGORITHM;
perfTestPasswordHashins(iterations, derivedKeySize, providerId, algorithm);
}
@Test
public void testPbkdf512() {
int iterations = 210 * 1000;
int derivedKeySize = 512;
String providerId = Pbkdf2Sha512PasswordHashProviderFactory.ID;
String algorithm = Pbkdf2Sha512PasswordHashProviderFactory.PBKDF2_ALGORITHM;
perfTestPasswordHashins(iterations, derivedKeySize, providerId, algorithm);
}
private void perfTestPasswordHashins(int iterations, int derivedKeySize, String providerId, String algorithm) {
Pbkdf2PasswordHashProvider provider = new Pbkdf2PasswordHashProvider(
providerId,
algorithm,
iterations,
0,
derivedKeySize);
perfTest(new Runnable() {
@Override
public void run() {
provider.encode("password", -1);
}
}, "testPbkdf512", 1);
}
private KeyPair generateKeys(int size) { private KeyPair generateKeys(int size) {
KeyPair keyPair; KeyPair keyPair;

View file

@ -17,7 +17,7 @@ public class Pbkdf2Sha256PasswordHashProviderFactory extends AbstractPbkdf2Passw
@Override @Override
public PasswordHashProvider create(KeycloakSession session) { public PasswordHashProvider create(KeycloakSession session) {
return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM, DEFAULT_ITERATIONS, getMaxPaddingLength()); return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM, DEFAULT_ITERATIONS, getMaxPaddingLength(), 256);
} }
@Override @Override

View file

@ -278,7 +278,13 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest {
} }
private void assertEncoded(PasswordCredentialModel credential, String password, byte[] salt, String algorithm, int iterations, boolean expectedSuccess) throws Exception { private void assertEncoded(PasswordCredentialModel credential, String password, byte[] salt, String algorithm, int iterations, boolean expectedSuccess) throws Exception {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, 512); int keyLength = 512;
if (Pbkdf2Sha256PasswordHashProviderFactory.ID.equals(credential.getPasswordCredentialData().getAlgorithm())) {
keyLength = 256;
}
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength);
byte[] key = SecretKeyFactory.getInstance(algorithm).generateSecret(spec).getEncoded(); byte[] key = SecretKeyFactory.getInstance(algorithm).generateSecret(spec).getEncoded();
if (expectedSuccess) { if (expectedSuccess) {
assertEquals(Base64.encodeBytes(key), credential.getPasswordSecretData().getValue()); assertEquals(Base64.encodeBytes(key), credential.getPasswordSecretData().getValue());