mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 15:02:05 -03:30
Add ECDSA as a valid key type that should return EC public key
Closes #42588 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
9f9f5ae97a
commit
5732946388
@ -33,6 +33,7 @@ public class JavaAlgorithm {
|
||||
public static final String Ed25519 = "Ed25519";
|
||||
public static final String Ed448 = "Ed448";
|
||||
public static final String AES = "AES";
|
||||
public static final String ECDSA = "ECDSA";
|
||||
|
||||
public static final String SHA256 = "SHA-256";
|
||||
public static final String SHA384 = "SHA-384";
|
||||
@ -135,6 +136,7 @@ public class JavaAlgorithm {
|
||||
case KeyType.RSA:
|
||||
return KeyType.RSA;
|
||||
case KeyType.EC:
|
||||
case ECDSA:
|
||||
return KeyType.EC;
|
||||
case Algorithm.EdDSA:
|
||||
case Algorithm.Ed448:
|
||||
|
||||
@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -29,6 +30,7 @@ import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
@ -221,6 +223,7 @@ public abstract class AbstractClientAuthSignedJWTTest extends AbstractKeycloakTe
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
.clientId("client3")
|
||||
.directAccessGrants()
|
||||
.redirectUris(OAuthClient.APP_ROOT + "/auth")
|
||||
.authenticatorType(JWTClientAuthenticator.PROVIDER_ID)
|
||||
.build();
|
||||
|
||||
@ -230,22 +233,7 @@ public abstract class AbstractClientAuthSignedJWTTest extends AbstractKeycloakTe
|
||||
}
|
||||
|
||||
public void testCodeToTokenRequestSuccess(String algorithm) throws Exception {
|
||||
oauth.clientId("client2");
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
EventRepresentation loginEvent = events.expectLogin()
|
||||
.client("client2")
|
||||
.assertEvent();
|
||||
|
||||
String code = oauth.parseLoginResponse().getCode();
|
||||
AccessTokenResponse response = doAccessTokenRequest(code, getClient2SignedJWT(algorithm));
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
oauth.verifyToken(response.getAccessToken());
|
||||
oauth.parseRefreshToken(response.getRefreshToken());
|
||||
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId())
|
||||
.client("client2")
|
||||
.detail(Details.CLIENT_AUTH_METHOD, JWTClientAuthenticator.PROVIDER_ID)
|
||||
.assertEvent();
|
||||
testCodeToTokenRequestSuccess("client2", getClient2KeyPair(), algorithm, null);
|
||||
}
|
||||
|
||||
public void testCodeToTokenRequestSuccessForceAlgInClient(String algorithm) throws Exception {
|
||||
@ -294,43 +282,62 @@ public abstract class AbstractClientAuthSignedJWTTest extends AbstractKeycloakTe
|
||||
}
|
||||
}
|
||||
|
||||
public void testUploadCertificatePEM(KeyPair keyPair, String algorithm, String curve) throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.BCFKS);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.BCFKS, "clientkey", "pwd2", "keypass", keyPair);
|
||||
try {
|
||||
Path tempFile = Files.createTempFile("cert_", ".pem");
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(tempFile)) {
|
||||
writer.write(ksInfo.getCertificateInfo().getCertificate());
|
||||
}
|
||||
testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.CERTIFICATE_PEM,
|
||||
tempFile.toFile().getAbsolutePath(), "undefined", "undefined");
|
||||
Files.delete(tempFile);
|
||||
|
||||
testCodeToTokenRequestSuccess("client3", keyPair, algorithm, curve);
|
||||
} finally {
|
||||
ksInfo.getKeystoreFile().delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void testUploadPublicKeyPem(KeyPair keyPair, String algorithm, String curve) throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.BCFKS);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.BCFKS, "clientkey", "pwd2", "keypass", keyPair);
|
||||
try {
|
||||
Path tempFile = Files.createTempFile("pubkey_", ".pem");
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(tempFile)) {
|
||||
writer.write(ksInfo.getCertificateInfo().getPublicKey());
|
||||
}
|
||||
testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.PUBLIC_KEY_PEM,
|
||||
tempFile.toFile().getAbsolutePath(), "undefined", "undefined");
|
||||
Files.delete(tempFile);
|
||||
|
||||
testCodeToTokenRequestSuccess("client3", keyPair, algorithm, curve);
|
||||
} finally {
|
||||
ksInfo.getKeystoreFile().delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void testCodeToTokenRequestSuccess(String algorithm, boolean useJwksUri) throws Exception {
|
||||
testCodeToTokenRequestSuccess(algorithm, null, useJwksUri);
|
||||
}
|
||||
|
||||
private KeyPair setupKeyPair(ClientRepresentation clientRepresentation, ClientResource clientResource,
|
||||
String algorithm, String curve, boolean useJwksUri) throws Exception {
|
||||
if (useJwksUri) {
|
||||
return setupJwksUrl(algorithm, curve, true, false, null, clientRepresentation, clientResource);
|
||||
} else {
|
||||
return setupJwks(algorithm, curve, clientRepresentation, clientResource);
|
||||
}
|
||||
}
|
||||
|
||||
protected void testCodeToTokenRequestSuccess(String algorithm, String curve, boolean useJwksUri) throws Exception {
|
||||
ClientRepresentation clientRepresentation = app2;
|
||||
ClientResource clientResource = getClient(testRealm.getRealm(), clientRepresentation.getId());
|
||||
clientRepresentation = clientResource.toRepresentation();
|
||||
try {
|
||||
// setup Jwks
|
||||
KeyPair keyPair;
|
||||
if (useJwksUri) {
|
||||
keyPair = setupJwksUrl(algorithm, curve, true, false, null, clientRepresentation, clientResource);
|
||||
} else {
|
||||
keyPair = setupJwks(algorithm, curve, clientRepresentation, clientResource);
|
||||
}
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
|
||||
// test
|
||||
oauth.clientId("client2");
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
EventRepresentation loginEvent = events.expectLogin()
|
||||
.client("client2")
|
||||
.assertEvent();
|
||||
|
||||
String code = oauth.parseLoginResponse().getCode();
|
||||
AccessTokenResponse response = doAccessTokenRequest(code,
|
||||
createSignedRequestToken("client2", getRealmInfoUrl(), privateKey, publicKey, algorithm, curve));
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
oauth.verifyToken(response.getAccessToken());
|
||||
oauth.parseRefreshToken(response.getRefreshToken());
|
||||
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId())
|
||||
.client("client2")
|
||||
.detail(Details.CLIENT_AUTH_METHOD, JWTClientAuthenticator.PROVIDER_ID)
|
||||
.assertEvent();
|
||||
KeyPair keyPair = setupKeyPair(clientRepresentation, clientResource, algorithm, curve, useJwksUri);
|
||||
testCodeToTokenRequestSuccess("client2", keyPair, algorithm, curve);
|
||||
} finally {
|
||||
// Revert jwks settings
|
||||
if (useJwksUri) {
|
||||
@ -341,6 +348,30 @@ public abstract class AbstractClientAuthSignedJWTTest extends AbstractKeycloakTe
|
||||
}
|
||||
}
|
||||
|
||||
protected void testCodeToTokenRequestSuccess(String clientId, KeyPair keyPair, String algorithm, String curve) throws Exception {
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
|
||||
// test
|
||||
oauth.realm("test").clientId(clientId);
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
EventRepresentation loginEvent = events.expectLogin()
|
||||
.client(clientId)
|
||||
.assertEvent();
|
||||
|
||||
String code = oauth.parseLoginResponse().getCode();
|
||||
AccessTokenResponse response = doAccessTokenRequest(code,
|
||||
createSignedRequestToken(clientId, getRealmInfoUrl(), privateKey, publicKey, algorithm, curve));
|
||||
|
||||
assertEquals(200, response.getStatusCode());
|
||||
oauth.verifyToken(response.getAccessToken());
|
||||
oauth.parseRefreshToken(response.getRefreshToken());
|
||||
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId())
|
||||
.client(clientId)
|
||||
.detail(Details.CLIENT_AUTH_METHOD, JWTClientAuthenticator.PROVIDER_ID)
|
||||
.assertEvent();
|
||||
}
|
||||
|
||||
protected void testDirectGrantRequestSuccess(String algorithm) throws Exception {
|
||||
ClientRepresentation clientRepresentation = app2;
|
||||
ClientResource clientResource = getClient(testRealm.getRealm(), clientRepresentation.getId());
|
||||
|
||||
@ -19,6 +19,7 @@ package org.keycloak.testsuite.oauth;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.testsuite.util.KeyUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
||||
@ -35,6 +36,26 @@ public class ClientAuthEdDSASignedJWTTest extends AbstractClientAuthSignedJWTTes
|
||||
testCodeToTokenRequestSuccess(Algorithm.EdDSA, Algorithm.Ed25519, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadCertificatePemEd25519() throws Exception {
|
||||
testUploadCertificatePEM(KeyUtils.generateEdDSAKey(Algorithm.Ed25519), Algorithm.EdDSA, Algorithm.Ed25519);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadCertificatePemEd448() throws Exception {
|
||||
testUploadCertificatePEM(KeyUtils.generateEdDSAKey(Algorithm.Ed448), Algorithm.EdDSA, Algorithm.Ed448);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadPublicKeyPemEd25519() throws Exception {
|
||||
testUploadPublicKeyPem(KeyUtils.generateEdDSAKey(Algorithm.Ed25519), Algorithm.EdDSA, Algorithm.Ed25519);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadPublicKeyPemEd448() throws Exception {
|
||||
testUploadPublicKeyPem(KeyUtils.generateEdDSAKey(Algorithm.Ed448), Algorithm.EdDSA, Algorithm.Ed448);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKeyAlgorithmFromJwaAlgorithm(String jwaAlgorithm, String curve) {
|
||||
if (!Algorithm.EdDSA.equals(jwaAlgorithm)) {
|
||||
|
||||
@ -46,6 +46,7 @@ import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.util.ClientManager;
|
||||
import org.keycloak.testsuite.util.KeystoreUtils;
|
||||
import org.keycloak.testsuite.util.KeyUtils;
|
||||
import org.keycloak.testsuite.util.SignatureSignerUtil;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
|
||||
@ -361,30 +362,53 @@ public class ClientAuthSignedJWTTest extends AbstractClientAuthSignedJWTTest {
|
||||
public void testUploadKeystoreJKS() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.JKS);
|
||||
testUploadKeystore("JKS", generatedKeystoreClient1.getKeystoreFile().getAbsolutePath(), "clientkey", "storepass");
|
||||
testCodeToTokenRequestSuccess("client3", keyPairClient1, Algorithm.RS256, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadKeystorePKCS12() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.PKCS12);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.PKCS12, "clientkey", "pwd2", "keypass");
|
||||
testUploadKeystore(KeystoreFormat.PKCS12.toString(), ksInfo.getKeystoreFile().getAbsolutePath(), "clientkey", "pwd2");
|
||||
KeyPair keyPair = org.keycloak.common.util.KeyUtils.generateRsaKeyPair(2048);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.PKCS12, "clientkey", "pwd2", "keypass", keyPair);
|
||||
try {
|
||||
testUploadKeystore(KeystoreFormat.PKCS12.toString(), ksInfo.getKeystoreFile().getAbsolutePath(), "clientkey", "pwd2");
|
||||
testCodeToTokenRequestSuccess("client3", keyPair, Algorithm.RS256, null);
|
||||
} finally {
|
||||
ksInfo.getKeystoreFile().delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadKeystoreBCFKS() throws Exception {
|
||||
KeystoreUtils.assumeKeystoreTypeSupported(KeystoreFormat.BCFKS);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.BCFKS, "clientkey", "pwd2", "keypass");
|
||||
testUploadKeystore(KeystoreFormat.BCFKS.toString(), ksInfo.getKeystoreFile().getAbsolutePath(), "clientkey", "pwd2");
|
||||
KeyPair keyPair = org.keycloak.common.util.KeyUtils.generateRsaKeyPair(2048);
|
||||
KeystoreUtils.KeystoreInfo ksInfo = KeystoreUtils.generateKeystore(folder, KeystoreFormat.BCFKS, "clientkey", "pwd2", "keypass", keyPair);
|
||||
try {
|
||||
testUploadKeystore(KeystoreFormat.BCFKS.toString(), ksInfo.getKeystoreFile().getAbsolutePath(), "clientkey", "pwd2");
|
||||
testCodeToTokenRequestSuccess("client3", keyPair, Algorithm.RS256, null);
|
||||
} finally {
|
||||
ksInfo.getKeystoreFile().delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadCertificatePEM() throws Exception {
|
||||
testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.CERTIFICATE_PEM, "client-auth-test/certificate.pem", "undefined", "undefined");
|
||||
public void testUploadCertificatePemRsa() throws Exception {
|
||||
testUploadCertificatePEM(org.keycloak.common.util.KeyUtils.generateRsaKeyPair(2048), Algorithm.RS256, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadPublicKeyPEM() throws Exception {
|
||||
testUploadKeystore(org.keycloak.services.resources.admin.ClientAttributeCertificateResource.PUBLIC_KEY_PEM, "client-auth-test/publickey.pem", "undefined", "undefined");
|
||||
public void testUploadCertificatePemEcdsa() throws Exception {
|
||||
testUploadCertificatePEM(KeyUtils.generateECKey(Algorithm.ES256), Algorithm.ES256, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadPublicKeyPemRsa() throws Exception {
|
||||
testUploadPublicKeyPem(org.keycloak.common.util.KeyUtils.generateRsaKeyPair(2048), Algorithm.RS256, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadPublicKeyPemEcdsa() throws Exception {
|
||||
testUploadPublicKeyPem(KeyUtils.generateECKey(Algorithm.ES256), Algorithm.ES256, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXTCCAkWgAwIBAgIJAIzE3vQp7EQWMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTYwMjI5MDgzMDU0WhcNNDMwNzE2MDgzMDU0WjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAp1+GzdEkt2FZbISXYO12503FL6Oh8s4+tJ2fE66N8IezhugP8xiySDfW
|
||||
TEMaO5Z2TaTnQQoF9SSZ9Edq1GPxpBX0cdkCOBopEGdlb3hUYDeMaDMs18KGemUc
|
||||
Fj+CWB5VVcbmWMJ36WCz7FC+Oe38tmujR1AJpJL3pwqazyWIZzPqX8rW+rrNPGKP
|
||||
C96oBPZMb4RJWivLBJi/o5MGSpo1sJNtxyF4zUUI00LX0wZAV1HH1XErd1Vz41on
|
||||
nmB+tj9nevVRR4rDV280IELp9Ud0PIb3w843uJtwfSAwVG0pT6hv1VBDrBxTS08N
|
||||
dPU8CtkQAXzCCr8nqfAbUFOhcWRQgQIDAQABo1AwTjAdBgNVHQ4EFgQUFE+uUZAI
|
||||
n57ArEylqhCmHkAenTEwHwYDVR0jBBgwFoAUFE+uUZAIn57ArEylqhCmHkAenTEw
|
||||
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApkgD3OCtw3+sk7GR1YaJ
|
||||
xNd8HT+fxXmnLqGnCQWX8lRIg5vj1PDMRev6vlIK3JfQV3zajcpKFfpy96klWsJy
|
||||
ZLYBVW2QOtMzDdQ9I8dS4Pn/SJ/Vo/M/ucfY4ttcuUL3oQCrI/c/u9tcamGMfbwd
|
||||
658MlXrUvt4B6qXY5AbgUvYR25P86uw7hSFMq5tQftNQsLbOh2FEeIiKhpgI7w8S
|
||||
SPajaWjUXsfHc5H7f9MciE2NS1Vd3AViGrVWP1rgQ1Iv0UyQVQrnjmIs12ENJmTd
|
||||
5lDqra5FJhaO7+RUG6er8n8HwXzhHkPmezGqtxWKikjitqvDY9prB3omJSa4Led+
|
||||
AQ==
|
||||
-----END CERTIFICATE-----
|
||||
@ -1,9 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApnZ/E2BUULHjsRiSnEgZ
|
||||
4vGe15BRqZPdkHR+NcvVYpThc7JqY6nZrdrwO9sOjlMC5e2Q18Fypi4KbJpGSe9r
|
||||
0DPgcbPsHSoe2xFO3M8XBE0DyoRblaQFhe6p/sj3ak32k2zn+fMZUmlx/MTNQh1I
|
||||
Cki7So0NDCBXt8XGZNnEyvKeXOUZP5qicP9KxVAQiWJvlkaTjc8rrRTmf+HWw/Qf
|
||||
gQC0tzBRpa7T+RpW9O+rnWfOaNfTkTb9itIc+ZOa2Z4iidZ7+ifMOp9cNT641Wb6
|
||||
iYqJ2ufqY+msxI54tYM1tPgGS7r4SnCwmnqTaO383wXUl8TQ7qStmAWIepV3nNyu
|
||||
AQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
Loading…
x
Reference in New Issue
Block a user