From bf81d7252ce0682b0f6941c15ca09bacb11e090c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sun, 15 Sep 2024 21:05:27 +0200 Subject: [PATCH] Allow to store OIDC JWT creds in PKCS12 keystore --- ...ecurity-oidc-code-flow-authentication.adoc | 2 +- ...urity-openid-connect-client-reference.adoc | 2 +- extensions/oidc-client/deployment/pom.xml | 2 ++ ...tCredentialsJwtPrivateP12KeyStoreTest.java | 34 ++++++++++++++++++ ...tials-jwt-private-p12-key-store.properties | 9 +++++ .../src/test/resources/keystore.pkcs12 | Bin 0 -> 2748 bytes .../oidc/common/runtime/OidcCommonUtils.java | 10 ++++-- 7 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientCredentialsJwtPrivateP12KeyStoreTest.java create mode 100644 extensions/oidc-client/deployment/src/test/resources/application-oidc-client-credentials-jwt-private-p12-key-store.properties create mode 100755 extensions/oidc-client/deployment/src/test/resources/keystore.pkcs12 diff --git a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc index c8330c4b7c90a..dfd7245c4b89c 100644 --- a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc @@ -197,7 +197,7 @@ quarkus.oidc.credentials.jwt.key-file=privateKey.pem ---- quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/ quarkus.oidc.client-id=quarkus-app -quarkus.oidc.credentials.jwt.key-store-file=keystore.jks +quarkus.oidc.credentials.jwt.key-store-file=keystore.pkcs12 quarkus.oidc.credentials.jwt.key-store-password=mypassword quarkus.oidc.credentials.jwt.key-password=mykeypassword diff --git a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc index 69c2fa0db1252..83ca2be79afc5 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -786,7 +786,7 @@ quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem ---- quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app -quarkus.oidc-client.credentials.jwt.key-store-file=keystore.jks +quarkus.oidc-client.credentials.jwt.key-store-file=keystore.pkcs12 quarkus.oidc-client.credentials.jwt.key-store-password=mypassword quarkus.oidc-client.credentials.jwt.key-password=mykeypassword diff --git a/extensions/oidc-client/deployment/pom.xml b/extensions/oidc-client/deployment/pom.xml index 9dd676e25f712..69e68bbdd88dd 100644 --- a/extensions/oidc-client/deployment/pom.xml +++ b/extensions/oidc-client/deployment/pom.xml @@ -93,6 +93,7 @@ true keystore.jks + keystore.pkcs12 @@ -100,6 +101,7 @@ false keystore.jks + keystore.pkcs12 diff --git a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientCredentialsJwtPrivateP12KeyStoreTest.java b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientCredentialsJwtPrivateP12KeyStoreTest.java new file mode 100644 index 0000000000000..29a9cb1205375 --- /dev/null +++ b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientCredentialsJwtPrivateP12KeyStoreTest.java @@ -0,0 +1,34 @@ +package io.quarkus.oidc.client; + +import static org.hamcrest.Matchers.equalTo; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.QuarkusTestResource; +import io.restassured.RestAssured; + +@QuarkusTestResource(KeycloakRealmClientCredentialsJwtPrivateKeyStoreManager.class) +public class OidcClientCredentialsJwtPrivateP12KeyStoreTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(OidcClientResource.class, ProtectedResource.class) + .addAsResource("application-oidc-client-credentials-jwt-private-p12-key-store.properties", + "application.properties") + .addAsResource("exportedCertificate.pem") + .addAsResource("exportedPrivateKey.pem") + .addAsResource("keystore.pkcs12")); + + @Test + public void testClientCredentialsToken() { + String token = RestAssured.when().get("/client/token").body().asString(); + RestAssured.given().auth().oauth2(token) + .when().get("/protected") + .then() + .statusCode(200) + .body(equalTo("service-account-quarkus-app")); + } +} diff --git a/extensions/oidc-client/deployment/src/test/resources/application-oidc-client-credentials-jwt-private-p12-key-store.properties b/extensions/oidc-client/deployment/src/test/resources/application-oidc-client-credentials-jwt-private-p12-key-store.properties new file mode 100644 index 0000000000000..6f549e1657d1a --- /dev/null +++ b/extensions/oidc-client/deployment/src/test/resources/application-oidc-client-credentials-jwt-private-p12-key-store.properties @@ -0,0 +1,9 @@ +quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus6/ +quarkus.oidc.client-id=quarkus-app + +quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url} +quarkus.oidc-client.client-id=${quarkus.oidc.client-id} +quarkus.oidc-client.credentials.jwt.key-store-file=keystore.pkcs12 +quarkus.oidc-client.credentials.jwt.key-store-password=password +quarkus.oidc-client.credentials.jwt.key-id=keycloak +quarkus.oidc-client.credentials.jwt.key-password=password diff --git a/extensions/oidc-client/deployment/src/test/resources/keystore.pkcs12 b/extensions/oidc-client/deployment/src/test/resources/keystore.pkcs12 new file mode 100755 index 0000000000000000000000000000000000000000..2aa875303174ef4f38dbd6e74a3f4b6cdce3d27e GIT binary patch literal 2748 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 d!-R=6V fileType, Path storePath) if (fileType.isPresent()) { return fileType.get().toUpperCase(); } - final String pathName = storePath.toString(); + return inferKeyStoreTypeFromFileExtension(storePath.toString()); + } + + private static String inferKeyStoreTypeFromFileExtension(String pathName) { if (pathName.endsWith(".p12") || pathName.endsWith(".pkcs12") || pathName.endsWith(".pfx")) { return "PKCS12"; } else { @@ -390,8 +393,9 @@ public static Key clientJwtKey(Credentials creds) { key = KeyUtils.readSigningKey(creds.jwt.getKeyFile().get(), creds.jwt.keyId.orElse(null), getSignatureAlgorithm(creds, SignatureAlgorithm.RS256)); } else if (creds.jwt.keyStoreFile.isPresent()) { - KeyStore ks = KeyStore.getInstance("JKS"); - InputStream is = ResourceUtils.getResourceStream(creds.jwt.keyStoreFile.get()); + var keyStoreFile = creds.jwt.keyStoreFile.get(); + KeyStore ks = KeyStore.getInstance(inferKeyStoreTypeFromFileExtension(keyStoreFile)); + InputStream is = ResourceUtils.getResourceStream(keyStoreFile); if (creds.jwt.keyStorePassword.isPresent()) { ks.load(is, creds.jwt.keyStorePassword.get().toCharArray());