KEYCLOAK-19723 Existing ECDSA key provider's key pair is not regenerated when its curve is changed on Admin Console

This commit is contained in:
Takashi Norimatsu 2021-11-02 17:33:45 +09:00 committed by Marek Posolda
parent 5740e158e3
commit d0493b4306
2 changed files with 85 additions and 3 deletions

View file

@ -28,7 +28,10 @@ import org.keycloak.models.RealmModel;
import org.keycloak.provider.ConfigurationValidationHelper;
import org.keycloak.provider.ProviderConfigProperty;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.interfaces.ECPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
public class GeneratedEcdsaKeyProviderFactory extends AbstractEcdsaKeyProviderFactory {
@ -103,7 +106,7 @@ public class GeneratedEcdsaKeyProviderFactory extends AbstractEcdsaKeyProviderFa
generateKeys(model, ecInNistRep);
logger.debugv("Generated keys for {0}", realm.getName());
} else {
String currentEc = model.get(ECDSA_ELLIPTIC_CURVE_KEY);
String currentEc = getCurveFromPublicKey(model.getConfig().getFirst(GeneratedEcdsaKeyProviderFactory.ECDSA_PUBLIC_KEY_KEY));
if (!ecInNistRep.equals(currentEc)) {
generateKeys(model, ecInNistRep);
logger.debugv("Elliptic Curve changed, generating new keys for {0}", realm.getName());
@ -123,4 +126,14 @@ public class GeneratedEcdsaKeyProviderFactory extends AbstractEcdsaKeyProviderFa
}
}
private String getCurveFromPublicKey(String publicEcdsaKeyBase64Encoded) {
try {
KeyFactory kf = KeyFactory.getInstance("EC");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.decode(publicEcdsaKeyBase64Encoded));
ECPublicKey ecKey = (ECPublicKey) kf.generatePublic(publicKeySpec);
return "P-" + ecKey.getParams().getCurve().getField().getFieldSize();
} catch (Throwable t) {
throw new ComponentValidationException("Failed to get EC from its public key", t);
}
}
}

View file

@ -17,9 +17,13 @@
package org.keycloak.testsuite.keys;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import java.security.KeyFactory;
import java.security.interfaces.ECPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import javax.ws.rs.WebApplicationException;
@ -28,8 +32,10 @@ import javax.ws.rs.core.Response;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.common.util.Base64;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.crypto.KeyType;
import org.keycloak.keys.AbstractEcdsaKeyProviderFactory;
import org.keycloak.keys.GeneratedEcdsaKeyProviderFactory;
import org.keycloak.keys.KeyProvider;
import org.keycloak.representations.idm.ComponentRepresentation;
@ -87,8 +93,8 @@ public class GeneratedEcdsaKeyProviderTest extends AbstractKeycloakTest {
// NIST.FIPS.186-4 Koblitz Curve over Binary Field
unsupportedEc("K-163");
}
private void supportedEc(String ecInNistRep) {
private String supportedEc(String ecInNistRep) {
long priority = System.currentTimeMillis();
ComponentRepresentation rep = createRep("valid", GeneratedEcdsaKeyProviderFactory.ID);
@ -127,6 +133,8 @@ public class GeneratedEcdsaKeyProviderTest extends AbstractKeycloakTest {
assertEquals(id, key.getProviderId());
assertEquals(KeyType.EC, key.getType());
assertEquals(priority, key.getProviderPriority());
return id; // created key's component id
}
private void unsupportedEc(String ecInNistRep) {
@ -152,6 +160,60 @@ public class GeneratedEcdsaKeyProviderTest extends AbstractKeycloakTest {
assertEquals(isEcAccepted, false);
}
@Test
public void changeCurveFromP256ToP384() throws Exception {
changeCurve("P-256", "P-384");
}
@Test
public void changeCurveFromP384ToP521() throws Exception {
changeCurve("P-384", "P-521");
}
@Test
public void changeCurveFromP521ToP256() throws Exception {
changeCurve("P-521", "P-256");
}
private void changeCurve(String FromEcInNistRep, String ToEcInNistRep) throws Exception {
String keyComponentId = supportedEc(FromEcInNistRep);
KeysMetadataRepresentation keys = adminClient.realm(TEST_REALM_NAME).keys().getKeyMetadata();
KeysMetadataRepresentation.KeyMetadataRepresentation originalKey = null;
for (KeyMetadataRepresentation k : keys.getKeys()) {
if (KeyType.EC.equals(k.getType()) && keyComponentId.equals(k.getProviderId())) {
originalKey = k;
break;
}
}
ComponentRepresentation createdRep = adminClient.realm(TEST_REALM_NAME).components().component(keyComponentId).toRepresentation();
createdRep.getConfig().putSingle(ECDSA_ELLIPTIC_CURVE_KEY, ToEcInNistRep);
adminClient.realm(TEST_REALM_NAME).components().component(keyComponentId).update(createdRep);
createdRep = adminClient.realm(TEST_REALM_NAME).components().component(keyComponentId).toRepresentation();
// stands for the number of properties in the key provider config
assertEquals(2, createdRep.getConfig().size());
assertEquals(ToEcInNistRep, createdRep.getConfig().getFirst(ECDSA_ELLIPTIC_CURVE_KEY));
keys = adminClient.realm(TEST_REALM_NAME).keys().getKeyMetadata();
KeysMetadataRepresentation.KeyMetadataRepresentation key = null;
for (KeyMetadataRepresentation k : keys.getKeys()) {
if (KeyType.EC.equals(k.getType()) && keyComponentId.equals(k.getProviderId())) {
key = k;
break;
}
}
assertNotNull(key);
assertEquals(keyComponentId, key.getProviderId());
assertNotEquals(originalKey.getKid(), key.getKid()); // kid is changed if key was regenerated
assertEquals(KeyType.EC, key.getType());
assertNotEquals(originalKey.getAlgorithm(), key.getAlgorithm());
assertEquals(ToEcInNistRep, AbstractEcdsaKeyProviderFactory.convertAlgorithmToECDomainParmNistRep(key.getAlgorithm()));
assertEquals(ToEcInNistRep, getCurveFromPublicKey(key.getPublicKey()));
}
protected ComponentRepresentation createRep(String name, String providerId) {
ComponentRepresentation rep = new ComponentRepresentation();
rep.setName(name);
@ -161,4 +223,11 @@ public class GeneratedEcdsaKeyProviderTest extends AbstractKeycloakTest {
rep.setConfig(new MultivaluedHashMap<>());
return rep;
}
private String getCurveFromPublicKey(String publicEcdsaKeyBase64Encoded) throws Exception {
KeyFactory kf = KeyFactory.getInstance("EC");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.decode(publicEcdsaKeyBase64Encoded));
ECPublicKey ecKey = (ECPublicKey) kf.generatePublic(publicKeySpec);
return "P-" + ecKey.getParams().getCurve().getField().getFieldSize();
}
}