From ab5f2a12b2a04ca05bf8a08a54939d3f052999e1 Mon Sep 17 00:00:00 2001 From: Prarthona Paul Date: Tue, 19 Mar 2024 12:41:57 -0400 Subject: [PATCH] [squash] added changes for community stability level deployment --- .../security/http/oidc/ElytronMessages.java | 26 +++-- .../http/oidc/JWKPublicKeySetExtractor.java | 76 ++++++++++++-- .../oidc/JWTClientCredentialsProvider.java | 3 +- .../security/http/oidc/JWTSigning.java | 46 --------- .../security/http/oidc/JWTSigningUtils.java | 12 +-- .../org/wildfly/security/http/oidc/Oidc.java | 13 ++- .../http/oidc/OidcClientConfiguration.java | 59 ++++++----- .../oidc/OidcClientConfigurationBuilder.java | 27 ++--- .../security/http/oidc/OidcClientContext.java | 40 ++++---- .../http/oidc/OidcJsonConfiguration.java | 64 +++++++----- .../http/oidc/OidcProviderMetadata.java | 2 +- .../http/oidc/OidcPublicKeyExtractor.java | 36 ------- .../http/oidc/OidcRequestAuthenticator.java | 99 ++++++++++--------- .../http/oidc/KeycloakConfiguration.java | 28 +++--- .../wildfly/security/http/oidc/OidcTest.java | 13 +-- 15 files changed, 285 insertions(+), 259 deletions(-) delete mode 100644 http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigning.java delete mode 100644 http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcPublicKeyExtractor.java diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java index 1cc55aafdc..eea5f06e2b 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/ElytronMessages.java @@ -20,7 +20,6 @@ import static org.jboss.logging.Logger.Level.DEBUG; import static org.jboss.logging.Logger.Level.ERROR; -import static org.jboss.logging.Logger.Level.INFO; import static org.jboss.logging.Logger.Level.WARN; import static org.jboss.logging.annotations.Message.NONE; @@ -236,19 +235,28 @@ interface ElytronMessages extends BasicLogger { IOException noMessageEntity(); @Message(id = 23057, value = "Invalid keystore configuration for signing Request Objects.") - IOException invalidKeyStoreConfiguration(); + RuntimeException invalidKeyStoreConfiguration(); - @Message(id = 23058, value = "The signature algorithm specified is not supported by the OpenID Provider.") - IOException invalidRequestObjectSignatureAlgorithm(); + @Message(id = 23058, value = "The request object signature algorithm specified is not supported by the OpenID Provider.") + RuntimeException invalidRequestObjectSignatureAlgorithm(); - @Message(id = 23059, value = "The encryption algorithm specified is not supported by the OpenID Provider.") - IOException invalidRequestObjectEncryptionAlgorithm(); + @Message(id = 23059, value = "The request object encryption algorithm specified is not supported by the OpenID Provider.") + RuntimeException invalidRequestObjectEncryptionAlgorithm(); - @Message(id = 23060, value = "The content encryption algorithm specified is not supported by the OpenID Provider.") - IOException invalidRequestObjectContentEncryptionAlgorithm(); + @Message(id = 23060, value = "The request object content encryption algorithm specified is not supported by the OpenID Provider.") + RuntimeException invalidRequestObjectContentEncryptionAlgorithm(); - @LogMessage(level = INFO) + @LogMessage(level = WARN) @Message(id = 23061, value = "The OpenID provider does not support request parameters. Sending the request using OAuth2 format.") void requestParameterNotSupported(); + + @Message(id = 23062, value = "Both request object encryption algorithm and request object content encryption algorithm must be configured to encrypt the request object.") + IllegalArgumentException invalidRequestEncryptionAlgorithmConfiguration(); + + @Message(id = 23063, value = "Failed to create the authentication request with request or request_uri parameter.") + RuntimeException unableToCreateRequestWithRequestParameter(); + + @Message (id = 23064, value = "Failed to connect with the OpenID provider.") + RuntimeException failedToConnectToOidcProvider(@Cause Exception cause); } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKPublicKeySetExtractor.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKPublicKeySetExtractor.java index 4069b73b7b..b2acd89da0 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKPublicKeySetExtractor.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWKPublicKeySetExtractor.java @@ -1,6 +1,6 @@ /* * JBoss, Home of Professional Open Source. - * Copyright 2020 Red Hat, Inc., and individual contributors + * Copyright 2024 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,26 +19,88 @@ package org.wildfly.security.http.oidc; import static org.apache.http.HttpHeaders.ACCEPT; +import static org.wildfly.security.http.oidc.ElytronMessages.log; import static org.wildfly.security.http.oidc.Oidc.JSON_CONTENT_TYPE; -import java.io.IOException; +import java.security.PublicKey; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import org.apache.http.client.methods.HttpGet; -import org.jose4j.lang.JoseException; +import org.wildfly.security.jose.jwk.JWK; +import org.wildfly.security.jose.jwk.JWKParser; import org.wildfly.security.jose.jwk.JsonWebKeySet; + /** * A public key locator that dynamically obtains the public key from an OpenID * provider by sending a request to the provider's {@code jwks_uri} when needed. * * @author Prarthona Paul * */ -public class JWKPublicKeySetExtractor implements OidcPublicKeyExtractor { +class JWKPublicKeySetExtractor implements PublicKeyLocator { + private Map currentKeys = new ConcurrentHashMap<>(); + + private volatile int lastRequestTime = 0; public JWKPublicKeySetExtractor() { } + @Override - public JsonWebKeySet extractPublicKeySet(OidcClientConfiguration config) throws IOException, JoseException { + public PublicKey getPublicKey(String kid, OidcClientConfiguration config) { + int minTimeBetweenRequests = config.getMinTimeBetweenJwksRequests(); + int publicKeyCacheTtl = config.getPublicKeyCacheTtl(); + int currentTime = getCurrentTime(); + + PublicKey publicKey = lookupCachedKey(publicKeyCacheTtl, currentTime, kid); + if (publicKey != null) { + return publicKey; + } + + synchronized (this) { + currentTime = getCurrentTime(); + if (currentTime > lastRequestTime + minTimeBetweenRequests) { + sendRequest(kid, config); + lastRequestTime = currentTime; + } else { + log.debug("Won't send request to jwks url. Last request time was " + lastRequestTime); + } + return lookupCachedKey(publicKeyCacheTtl, currentTime, kid); + } + + } + + @Override + public void reset(OidcClientConfiguration config) { + synchronized (this) { + sendRequest("enc", config); + lastRequestTime = getCurrentTime(); + } + } + + private PublicKey lookupCachedKey(int publicKeyCacheTtl, int currentTime, String kid) { + if (lastRequestTime + publicKeyCacheTtl > currentTime && kid != null) { + return currentKeys.get(kid); + } else { + return null; + } + } + + private static int getCurrentTime() { + return (int) (System.currentTimeMillis() / 1000); + } + + private void sendRequest(String kid, OidcClientConfiguration config) { HttpGet request = new HttpGet(config.getJwksUrl()); request.addHeader(ACCEPT, JSON_CONTENT_TYPE); - return Oidc.sendJsonHttpRequest(config, request, JsonWebKeySet.class); + try { + JWK[] jwkList = Oidc.sendJsonHttpRequest(config, request, JsonWebKeySet.class).getKeys(); + for (JWK jwk : jwkList) { + if (jwk.getPublicKeyUse().equals(kid)) { //JWTs are to be encrypted with public keys shared by the OpenID provider and decrypted by the private key they hold + currentKeys.clear(); + currentKeys.put(kid, new JWKParser(jwk).toPublicKey()); + } + } + } catch (OidcException e) { + log.error("Error when sending request to retrieve public keys", e); + } } - } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java index 5ea6383e7a..13df213373 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTClientCredentialsProvider.java @@ -19,6 +19,7 @@ package org.wildfly.security.http.oidc; import static org.wildfly.security.http.oidc.ElytronMessages.log; +import static org.wildfly.security.http.oidc.JWTSigningUtils.loadKeyPairFromKeyStore; import static org.wildfly.security.http.oidc.Oidc.CLIENT_ASSERTION; import static org.wildfly.security.http.oidc.Oidc.CLIENT_ASSERTION_TYPE; import static org.wildfly.security.http.oidc.Oidc.CLIENT_ASSERTION_TYPE_JWT; @@ -109,7 +110,7 @@ public void init(OidcClientConfiguration oidcClientConfiguration, Object credent clientKeyAlias = oidcClientConfiguration.getResourceName(); } - KeyPair keyPair = new JWTSigningUtils().loadKeyPairFromKeyStore(clientKeyStoreFile, clientKeyStorePassword, clientKeyPassword, clientKeyAlias, clientKeyStoreType); + KeyPair keyPair = loadKeyPairFromKeyStore(clientKeyStoreFile, clientKeyStorePassword, clientKeyPassword, clientKeyAlias, clientKeyStoreType); setupKeyPair(keyPair); this.tokenTimeout = asInt(cfg, "token-timeout", 10); } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigning.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigning.java deleted file mode 100644 index 0b9207934c..0000000000 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigning.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2023 Red Hat, Inc., and individual contributors - * as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.wildfly.security.http.oidc; - -import java.io.InputStream; -import java.security.KeyPair; - -/** - * An interface to obtain the KeyPair from a keystore file. - * - * @author Prarthona Paul - */ - -public interface JWTSigning { - /** - * @param keyStoreFile the path to the keystore file - * @param storePassword the password for the keystore file - * @param keyPassword the password for the key we would like ot extract from the keystore - * @param keyAlias the alias for the key that uniquely identifies it - * @param keyStoreType the type of keystore we are trying to access - * @return the private-public keypair extracted from the keystore - */ - KeyPair loadKeyPairFromKeyStore(String keyStoreFile, String storePassword, String keyPassword, String keyAlias, String keyStoreType); - - /** - * @param keystoreFile the path the keystore file we are trying to access - * @return the contents of the file as an inputStream - */ - InputStream findFile(String keystoreFile); -} diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java index 4725e0d5e8..5fafee0e46 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/JWTSigningUtils.java @@ -35,18 +35,17 @@ * @author Prarthona Paul */ -public class JWTSigningUtils implements JWTSigning{ - public JWTSigningUtils() {} +public class JWTSigningUtils { + private JWTSigningUtils() {} - @Override - public KeyPair loadKeyPairFromKeyStore(String keyStoreFile, String storePassword, String keyPassword, String keyAlias, String keyStoreType) { + public static KeyPair loadKeyPairFromKeyStore(String keyStoreFile, String storePassword, String keyPassword, String keyAlias, String keyStoreType) { InputStream stream = findFile(keyStoreFile); try { KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(stream, storePassword.toCharArray()); PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray()); if (privateKey == null) { - log.unableToLoadKeyWithAlias(keyAlias); + throw log.unableToLoadKeyWithAlias(keyAlias); } PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey(); return new KeyPair(publicKey, privateKey); @@ -55,8 +54,7 @@ public KeyPair loadKeyPairFromKeyStore(String keyStoreFile, String storePassword } } - @Override - public InputStream findFile(String keystoreFile) { + public static InputStream findFile(String keystoreFile) { if (keystoreFile.startsWith(PROTOCOL_CLASSPATH)) { String classPathLocation = keystoreFile.replace(PROTOCOL_CLASSPATH, ""); // try current class classloader first diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java index 36c5a763c0..44bdb9d382 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/Oidc.java @@ -45,6 +45,7 @@ public class Oidc { public static final String ACCEPT = "Accept"; + public static final String AUTHENTICATION_REQUEST_FORMAT = "authentication-request-format"; public static final String OIDC_NAME = "OIDC"; public static final String JSON_CONTENT_TYPE = "application/json"; public static final String HTML_CONTENT_TYPE = "text/html"; @@ -74,6 +75,14 @@ public class Oidc { public static final String PASSWORD = "password"; public static final String PROMPT = "prompt"; public static final String REQUEST = "request"; + public static final String REQUEST_OBJECT_CONTENT_ENCRYPTION_ALGORITHM = "request-object-content-encryption-algorithm"; + public static final String REQUEST_OBJECT_ENCRYPTION_ALGORITHM = "request-object-encryption-algorithm"; + public static final String REQUEST_OBJECT_SIGNING_ALGORITHM = "request-object-signing-algorithm"; + public static final String REQUEST_OBJECT_SIGNING_KEYSTORE_FILE = "request-object-signing-keystore-file"; + public static final String REQUEST_OBJECT_SIGNING_KEYSTORE_PASSWORD = "request-object-signing-keystore-password"; + public static final String REQUEST_OBJECT_SIGNING_KEY_PASSWORD = "request-object-signing-key-password"; + public static final String REQUEST_OBJECT_SIGNING_KEY_ALIAS = "request-object-signing-key-alias"; + public static final String REQUEST_OBJECT_SIGNING_KEYSTORE_TYPE = "request-object-signing-keystore-type"; public static final String REQUEST_URI = "request_uri"; public static final String SCOPE = "scope"; public static final String UI_LOCALES = "ui_locales"; @@ -213,9 +222,9 @@ public enum AuthenticationFormat { } /** - * Get the string value for this referral mode. + * Get the string value for this authentication format. * - * @return the string value for this referral mode + * @return the string value for this authentication format */ public String getValue() { return value; diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java index 5a1324137b..ec1c85090e 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfiguration.java @@ -133,15 +133,16 @@ public enum RelativeUrlsUsed { protected String tokenSignatureAlgorithm = DEFAULT_TOKEN_SIGNATURE_ALGORITHM; protected String authenticationRequestFormat; - protected String requestSignatureAlgorithm; - protected String requestEncryptAlgorithm; - protected String requestContentEncryptionMethod; + protected String requestObjectSignatureAlgorithm; + protected String requestObjectEncryptAlgorithm; + protected String requestObjectContentEncryptionMethod; protected String pushedAuthorizationRequestEndpoint; - protected String requestObjectSigningKeystoreFile; + protected String requestObjectSigningKeyStoreFile; protected String requestObjectSigningKeyStorePass; protected String requestObjectSigningKeyPass; protected String requestObjectSigningKeyAlias; - protected String requestObjectSigningKeystoreType; + protected String requestObjectSigningKeyStoreType; + protected JWKPublicKeySetExtractor encryptionPublicKeyLocator; public OidcClientConfiguration() { } @@ -699,36 +700,36 @@ public void setAuthenticationRequestFormat(String authenticationRequestFormat ) this.authenticationRequestFormat = authenticationRequestFormat; } - public String getRequestSignatureAlgorithm() { - return requestSignatureAlgorithm; + public String getRequestObjectSignatureAlgorithm() { + return requestObjectSignatureAlgorithm; } - public void setRequestSignatureAlgorithm(String requestSignatureAlgorithm) { - this.requestSignatureAlgorithm = requestSignatureAlgorithm; + public void setRequestObjectSignatureAlgorithm(String requestObjectSignatureAlgorithm) { + this.requestObjectSignatureAlgorithm = requestObjectSignatureAlgorithm; } - public String getRequestEncryptAlgorithm() { - return requestEncryptAlgorithm; + public String getRequestObjectEncryptAlgorithm() { + return requestObjectEncryptAlgorithm; } - public void setRequestEncryptAlgorithm(String requestEncryptAlgorithm) { - this.requestEncryptAlgorithm = requestEncryptAlgorithm; + public void setRequestObjectEncryptAlgorithm(String requestObjectEncryptAlgorithm) { + this.requestObjectEncryptAlgorithm = requestObjectEncryptAlgorithm; } - public String getRequestContentEncryptionMethod() { - return requestContentEncryptionMethod; + public String getRequestObjectContentEncryptionMethod() { + return requestObjectContentEncryptionMethod; } - public void setRequestContentEncryptionMethod(String requestContentEncryptionMethod) { - this.requestContentEncryptionMethod = requestContentEncryptionMethod; + public void setRequestObjectContentEncryptionMethod(String requestObjectContentEncryptionMethod) { + this.requestObjectContentEncryptionMethod = requestObjectContentEncryptionMethod; } - public String getRequestObjectSigningKeystoreFile() { - return requestObjectSigningKeystoreFile; + public String getRequestObjectSigningKeyStoreFile() { + return requestObjectSigningKeyStoreFile; } - public void setRequestObjectSigningKeystoreFile(String keyStoreFile) { - this.requestObjectSigningKeystoreFile = keyStoreFile; + public void setRequestObjectSigningKeyStoreFile(String keyStoreFile) { + this.requestObjectSigningKeyStoreFile = keyStoreFile; } public String getRequestObjectSigningKeyStorePassword() { @@ -747,12 +748,12 @@ public void setRequestObjectSigningKeyPassword(String pass) { this.requestObjectSigningKeyPass = pass; } - public String getRequestObjectSigningKeystoreType() { - return requestObjectSigningKeystoreType; + public String getRequestObjectSigningKeyStoreType() { + return requestObjectSigningKeyStoreType; } - public void setRequestObjectSigningKeystoreType(String type) { - this.requestObjectSigningKeystoreType = type; + public void setRequestObjectSigningKeyStoreType(String type) { + this.requestObjectSigningKeyStoreType = type; } public String getRequestObjectSigningKeyAlias() { @@ -770,4 +771,12 @@ public String getPushedAuthorizationRequestEndpoint() { public void setPushedAuthorizationRequestEndpoint(String url) { this.pushedAuthorizationRequestEndpoint = url; } + + public void setEncryptionPublicKeyLocator(JWKPublicKeySetExtractor publicKeySetExtractor) { + this.encryptionPublicKeyLocator = publicKeySetExtractor; + } + + public JWKPublicKeySetExtractor getEncryptionPublicKeyLocator() { + return this.encryptionPublicKeyLocator; + } } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java index 6b95391e59..8a0703f5d5 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientConfigurationBuilder.java @@ -110,25 +110,29 @@ protected OidcClientConfiguration internalBuild(final OidcJsonConfiguration oidc } else { oidcClientConfiguration.setAuthenticationRequestFormat(REQUEST_TYPE_OAUTH2.getValue()); } - if (oidcJsonConfiguration.getRequestSignatureAlgorithm() != null) { - oidcClientConfiguration.setRequestSignatureAlgorithm(oidcJsonConfiguration.getRequestSignatureAlgorithm()); + if (oidcJsonConfiguration.getRequestObjectSignatureAlgorithm() != null) { + oidcClientConfiguration.setRequestObjectSignatureAlgorithm(oidcJsonConfiguration.getRequestObjectSignatureAlgorithm()); } else { - oidcClientConfiguration.setRequestSignatureAlgorithm(NONE); + oidcClientConfiguration.setRequestObjectSignatureAlgorithm(NONE); } - if (oidcJsonConfiguration.getRequestEncryptAlgorithm() != null && oidcJsonConfiguration.getRequestContentEncryptionMethod() != null) { //both are required to encrypt the request object - oidcClientConfiguration.setRequestEncryptAlgorithm(oidcJsonConfiguration.getRequestEncryptAlgorithm()); - oidcClientConfiguration.setRequestContentEncryptionMethod(oidcJsonConfiguration.getRequestContentEncryptionMethod()); + if (oidcJsonConfiguration.getRequestEncryptAlgorithm() != null && oidcJsonConfiguration.getRequestObjectContentEncryptionMethod() != null) { //both are required to encrypt the request object + oidcClientConfiguration.setRequestObjectEncryptAlgorithm(oidcJsonConfiguration.getRequestEncryptAlgorithm()); + oidcClientConfiguration.setRequestObjectContentEncryptionMethod(oidcJsonConfiguration.getRequestObjectContentEncryptionMethod()); + JWKPublicKeySetExtractor encryptionPublicKeyLocator = new JWKPublicKeySetExtractor(); + oidcClientConfiguration.setEncryptionPublicKeyLocator(encryptionPublicKeyLocator); + } else if (oidcClientConfiguration.getRequestObjectEncryptAlgorithm() != null || oidcClientConfiguration.getRequestObjectContentEncryptionMethod() != null) { //if only one is specified, that is not correct + throw log.invalidRequestEncryptionAlgorithmConfiguration(); } if (oidcJsonConfiguration.getRequestObjectSigningKeyStoreFile() != null - && oidcJsonConfiguration.getRequestObjectSigningKeystorePassword() != null + && oidcJsonConfiguration.getRequestObjectSigningKeyStorePassword() != null && oidcJsonConfiguration.getRequestObjectSigningKeyPassword() != null && oidcJsonConfiguration.getRequestObjectSigningKeyAlias() != null) { - oidcClientConfiguration.setRequestObjectSigningKeystoreFile(oidcJsonConfiguration.getRequestObjectSigningKeyStoreFile()); - oidcClientConfiguration.setRequestObjectSigningKeyStorePassword(oidcJsonConfiguration.getRequestObjectSigningKeystorePassword()); + oidcClientConfiguration.setRequestObjectSigningKeyStoreFile(oidcJsonConfiguration.getRequestObjectSigningKeyStoreFile()); + oidcClientConfiguration.setRequestObjectSigningKeyStorePassword(oidcJsonConfiguration.getRequestObjectSigningKeyStorePassword()); oidcClientConfiguration.setRequestObjectSigningKeyPassword(oidcJsonConfiguration.getRequestObjectSigningKeyPassword()); oidcClientConfiguration.setRequestObjectSigningKeyAlias(oidcJsonConfiguration.getRequestObjectSigningKeyAlias()); - if (oidcJsonConfiguration.getRequestObjectSigningKeystoreType() != null) { - oidcClientConfiguration.setRequestObjectSigningKeystoreType(oidcJsonConfiguration.getRequestObjectSigningKeystoreType()); + if (oidcJsonConfiguration.getRequestObjectSigningKeyStoreType() != null) { + oidcClientConfiguration.setRequestObjectSigningKeyStoreType(oidcJsonConfiguration.getRequestObjectSigningKeyStoreType()); } } if (oidcJsonConfiguration.getPrincipalAttribute() != null) oidcClientConfiguration.setPrincipalAttribute(oidcJsonConfiguration.getPrincipalAttribute()); @@ -224,4 +228,5 @@ public static OidcJsonConfiguration loadOidcJsonConfiguration(InputStream is) { public static OidcClientConfiguration build(OidcJsonConfiguration oidcJsonConfiguration) { return new OidcClientConfigurationBuilder().internalBuild(oidcJsonConfiguration); } + } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java index 62424e7b5c..409f817ea4 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcClientContext.java @@ -537,43 +537,43 @@ public void setAuthenticationRequestFormat(String authFormat) { } @Override - public String getRequestSignatureAlgorithm() { - return delegate.getRequestSignatureAlgorithm(); + public String getRequestObjectSignatureAlgorithm() { + return delegate.getRequestObjectSignatureAlgorithm(); } @Override - public void setRequestSignatureAlgorithm(String requestSignature) { - delegate.setRequestSignatureAlgorithm(requestSignature); + public void setRequestObjectSignatureAlgorithm(String requestSignature) { + delegate.setRequestObjectSignatureAlgorithm(requestSignature); } @Override - public String getRequestEncryptAlgorithm() { - return delegate.getRequestEncryptAlgorithm(); + public String getRequestObjectEncryptAlgorithm() { + return delegate.getRequestObjectEncryptAlgorithm(); } @Override - public void setRequestEncryptAlgorithm(String algorithm) { - delegate.setRequestEncryptAlgorithm(algorithm); + public void setRequestObjectEncryptAlgorithm(String algorithm) { + delegate.setRequestObjectEncryptAlgorithm(algorithm); } @Override - public String getRequestContentEncryptionMethod() { - return delegate.requestContentEncryptionMethod; + public String getRequestObjectContentEncryptionMethod() { + return delegate.requestObjectContentEncryptionMethod; } @Override - public void setRequestContentEncryptionMethod (String enc) { - delegate.requestContentEncryptionMethod = enc; + public void setRequestObjectContentEncryptionMethod (String enc) { + delegate.requestObjectContentEncryptionMethod = enc; } @Override - public String getRequestObjectSigningKeystoreFile() { - return delegate.requestObjectSigningKeystoreFile; + public String getRequestObjectSigningKeyStoreFile() { + return delegate.requestObjectSigningKeyStoreFile; } @Override - public void setRequestObjectSigningKeystoreFile(String keyStoreFile) { - delegate.requestObjectSigningKeystoreFile = keyStoreFile; + public void setRequestObjectSigningKeyStoreFile(String keyStoreFile) { + delegate.requestObjectSigningKeyStoreFile = keyStoreFile; } @Override @@ -597,13 +597,13 @@ public void setRequestObjectSigningKeyPassword(String pass) { } @Override - public String getRequestObjectSigningKeystoreType() { - return delegate.requestObjectSigningKeystoreType; + public String getRequestObjectSigningKeyStoreType() { + return delegate.requestObjectSigningKeyStoreType; } @Override - public void setRequestObjectSigningKeystoreType(String type) { - delegate.requestObjectSigningKeystoreType = type; + public void setRequestObjectSigningKeyStoreType(String type) { + delegate.requestObjectSigningKeyStoreType = type; } @Override diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java index 8871e30ce0..1bfdb9daff 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcJsonConfiguration.java @@ -45,9 +45,11 @@ "register-node-at-startup", "register-node-period", "token-store", "adapter-state-cookie-path", "principal-attribute", "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests", "public-key-cache-ttl", - "ignore-oauth-query-parameter", "verify-token-audience", "token-signature-algorithm", "scope" + "ignore-oauth-query-parameter", "verify-token-audience", "token-signature-algorithm", "scope", "authentication-request-format", "request-object-signing-algorithm", "request-object-encryption-algorithm", - "request-object-content-encryption-algorithm" + "request-object-content-encryption-algorithm", "request-object-signing-keystore-file", + "request-object-signing-keystore-password","request-object-signing-key-password", "request-object-signing-key-alias", + "request-object-signing-keystore-type" }) public class OidcJsonConfiguration { @@ -63,16 +65,18 @@ public class OidcJsonConfiguration { protected String clientKeystore; @JsonProperty("client-keystore-password") protected String clientKeystorePassword; + @JsonProperty("client-key-password") + protected String clientKeyPassword; @JsonProperty("request-object-signing-keystore-file") protected String requestObjectSigningKeyStoreFile; @JsonProperty("request-object-signing-keystore-password") - protected String requestObjectSigningKeystorePassword; + protected String requestObjectSigningKeyStorePassword; @JsonProperty("request-object-signing-key-password") protected String requestObjectSigningKeyPassword; @JsonProperty("request-object-signing-key-alias") protected String requestObjectSigningKeyAlias; @JsonProperty("request-object-signing-keystore-type") - protected String requestObjectSigningKeystoreType; + protected String requestObjectSigningKeyStoreType; @JsonProperty("connection-pool-size") protected int connectionPoolSize = 20; @JsonProperty("always-refresh-token") @@ -155,13 +159,13 @@ public class OidcJsonConfiguration { protected String authenticationRequestFormat; @JsonProperty("request-object-signing-algorithm") - protected String requestSignatureAlgorithm; + protected String requestObjectSignatureAlgorithm; @JsonProperty("request-object-encryption-algorithm") - protected String requestEncryptAlgorithm; + protected String requestObjectEncryptAlgorithm; @JsonProperty("request-object-content-encryption-algorithm") - protected String requestContentEncryptionMethod; + protected String requestObjectContentEncryptionMethod; /** * The Proxy url to use for requests to the auth-server, configurable via the adapter config property {@code proxy-url}. @@ -216,12 +220,12 @@ public void setClientKeystore(String clientKeystore) { this.clientKeystore = clientKeystore; } - public String getRequestObjectSigningKeystoreType() { - return requestObjectSigningKeystoreType; + public String getRequestObjectSigningKeyStoreType() { + return requestObjectSigningKeyStoreType; } - public void setRequestObjectSigningKeystoreType(String type) { - this.requestObjectSigningKeystoreType = type; + public void setRequestObjectSigningKeyStoreType(String type) { + this.requestObjectSigningKeyStoreType = type; } public String getRequestObjectSigningKeyAlias() { @@ -240,16 +244,24 @@ public void setClientKeystorePassword(String clientKeystorePassword) { this.clientKeystorePassword = clientKeystorePassword; } + public String getClientKeyPassword() { + return clientKeyPassword; + } + public String getRequestObjectSigningKeyPassword() { return requestObjectSigningKeyPassword; } - public String getRequestObjectSigningKeystorePassword() { - return requestObjectSigningKeystorePassword; + public String getRequestObjectSigningKeyStorePassword() { + return requestObjectSigningKeyStorePassword; + } + + public void setClientKeyPassword(String clientKeyPassword) { + this.clientKeyPassword = clientKeyPassword; } - public void setRequestObjectSigningKeystorePassword(String requestObjectSigningKeystorePassword) { - this.requestObjectSigningKeystorePassword = requestObjectSigningKeystorePassword; + public void setRequestObjectSigningKeyStorePassword(String requestObjectSigningKeyStorePassword) { + this.requestObjectSigningKeyStorePassword = requestObjectSigningKeyStorePassword; } public void setRequestObjectSigningKeyPassword(String requestObjectSigningKeyPassword) { @@ -580,28 +592,28 @@ public void setAuthenticationRequestFormat(String authenticationRequestFormat) { this.authenticationRequestFormat = authenticationRequestFormat; } - public String getRequestSignatureAlgorithm() { - return requestSignatureAlgorithm; + public String getRequestObjectSignatureAlgorithm() { + return requestObjectSignatureAlgorithm; } - public void setRequestSignatureAlgorithm(String requestSignatureAlgorithm) { - this.requestSignatureAlgorithm = requestSignatureAlgorithm; + public void setRequestSignatureAlgorithm(String requestObjectSignatureAlgorithm) { + this.requestObjectSignatureAlgorithm = requestObjectSignatureAlgorithm; } public String getRequestEncryptAlgorithm() { - return requestEncryptAlgorithm; + return requestObjectEncryptAlgorithm; } - public void setRequestEncryptAlgorithm(String requestSignatureAlgorithm) { - this.requestEncryptAlgorithm = requestSignatureAlgorithm; + public void setRequestObjectEncryptAlgorithm(String requestObjectSignatureAlgorithm) { + this.requestObjectEncryptAlgorithm = requestObjectSignatureAlgorithm; } - public String getRequestContentEncryptionMethod() { - return requestContentEncryptionMethod; + public String getRequestObjectContentEncryptionMethod() { + return requestObjectContentEncryptionMethod; } - public void setRequestContentEncryptionMethod (String requestContentEncryptionMethod) { - this.requestContentEncryptionMethod = requestContentEncryptionMethod; + public void setRequestObjectContentEncryptionMethod (String requestObjectContentEncryptionMethod) { + this.requestObjectContentEncryptionMethod = requestObjectContentEncryptionMethod; } } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java index 6da737f5b1..af005e0031 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcProviderMetadata.java @@ -440,7 +440,7 @@ public String getPushedAuthorizationRequestEndpoint() { return pushedAuthorizationRequestEndpoint; } - public void setPushedAuthorizationRequestEndpoint (String url) { + public void setPushedAuthorizationRequestEndpoint(String url) { this.pushedAuthorizationRequestEndpoint = url; } diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcPublicKeyExtractor.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcPublicKeyExtractor.java deleted file mode 100644 index 75b5294341..0000000000 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcPublicKeyExtractor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2023 Red Hat, Inc., and individual contributors - * as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.wildfly.security.http.oidc; - -import org.wildfly.security.jose.jwk.JsonWebKeySet; - -/** - * An interface to send a GET request to the JWKSUrl of the OpenID - * provider and obtain the public key. - * - * @author Prarthona Paul - */ -public interface OidcPublicKeyExtractor { - /** - * @param config the OpenID Connect client configuration - * @return a Json Web Key Set with the public keys for the OpenID provider - */ - JsonWebKeySet extractPublicKeySet(OidcClientConfiguration config) throws Exception; - -} diff --git a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java index 1d3087cbfd..2ae0ef69d0 100644 --- a/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java +++ b/http/oidc/src/main/java/org/wildfly/security/http/oidc/OidcRequestAuthenticator.java @@ -58,6 +58,7 @@ import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyPair; +import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -82,8 +83,6 @@ import org.jose4j.keys.HmacKey; import org.jose4j.lang.JoseException; import org.wildfly.security.http.HttpConstants; -import org.wildfly.security.jose.jwk.JWK; -import org.wildfly.security.jose.jwk.JWKParser; /** @@ -225,7 +224,11 @@ protected String getRedirectUri(String state) { case REQUEST: if (deployment.getRequestParameterSupported()) { // add request objects into request parameter - createRequestWithRequestParameter(REQUEST, redirectUriBuilder, redirectUri, state, forwardedQueryParams); + try { + createRequestWithRequestParameter(REQUEST, redirectUriBuilder, redirectUri, state, forwardedQueryParams); + } catch (IOException | JoseException e) { + throw log.unableToCreateRequestWithRequestParameter(); + } } else { // send request as usual createOAuthRequest(redirectUriBuilder, redirectUri, state, forwardedQueryParams); @@ -234,7 +237,11 @@ protected String getRedirectUri(String state) { break; case REQUEST_URI: if (deployment.getRequestUriParameterSupported()) { - createRequestWithRequestParameter(REQUEST_URI, redirectUriBuilder, redirectUri, state, forwardedQueryParams); + try { + createRequestWithRequestParameter(REQUEST_URI, redirectUriBuilder, redirectUri, state, forwardedQueryParams); + } catch (IOException | JoseException e) { + throw log.unableToCreateRequestWithRequestParameter(); + } } else { // send request as usual createOAuthRequest(redirectUriBuilder, redirectUri, state, forwardedQueryParams); @@ -248,8 +255,6 @@ protected String getRedirectUri(String state) { return redirectUriBuilder.build().toString(); } catch (URISyntaxException e) { throw log.unableToCreateRedirectResponse(e); - } catch (IOException | JoseException | InvalidJwtException e) { - throw new RuntimeException(e); } } @@ -260,7 +265,7 @@ protected URIBuilder createOAuthRequest(URIBuilder redirectUriBuilder, String re return redirectUriBuilder; } - protected URIBuilder createRequestWithRequestParameter(String requestFormat, URIBuilder redirectUriBuilder, String redirectUri, String state, List forwardedQueryParams) throws JoseException, IOException, InvalidJwtException { + protected URIBuilder createRequestWithRequestParameter(String requestFormat, URIBuilder redirectUriBuilder, String redirectUri, String state, List forwardedQueryParams) throws JoseException, IOException { String request = convertToRequestParameter(redirectUriBuilder, redirectUri, state, forwardedQueryParams); switch (requestFormat) { @@ -522,7 +527,7 @@ private void addScopes(String scopes, Set allScopes) { } } - private String convertToRequestParameter(URIBuilder redirectUriBuilder, String redirectUri, String state, List forwardedQueryParams) throws IOException, JoseException { + private String convertToRequestParameter(URIBuilder redirectUriBuilder, String redirectUri, String state, List forwardedQueryParams) throws JoseException, IOException { redirectUriBuilder.addParameter(SCOPE, OIDC_SCOPE); JwtClaims jwtClaims = new JwtClaims(); @@ -541,24 +546,24 @@ private String convertToRequestParameter(URIBuilder redirectUriBuilder, String r JsonWebSignature signedRequest = signRequest(jwtClaims); // Encrypting optional - if (deployment.getRequestEncryptAlgorithm() != null && !deployment.getRequestEncryptAlgorithm().isEmpty() && - deployment.getRequestContentEncryptionMethod() != null && !deployment.getRequestContentEncryptionMethod().isEmpty()) { + if (deployment.getRequestObjectEncryptAlgorithm() != null && !deployment.getRequestObjectEncryptAlgorithm().isEmpty() && + deployment.getRequestObjectContentEncryptionMethod() != null && !deployment.getRequestObjectContentEncryptionMethod().isEmpty()) { return encryptRequest(signedRequest).getCompactSerialization(); } else { return signedRequest.getCompactSerialization(); } } - public KeyPair getkeyPair() throws IOException { - if (deployment.getRequestObjectSigningKeystoreFile().contains(PROTOCOL_CLASSPATH)) { - deployment.setRequestObjectSigningKeystoreFile(deployment.getRequestObjectSigningKeystoreFile().replace(PROTOCOL_CLASSPATH, Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath())); + private static KeyPair getkeyPair(OidcClientConfiguration deployment) throws IOException { + if (deployment.getRequestObjectSigningKeyStoreFile().contains(PROTOCOL_CLASSPATH)) { + deployment.setRequestObjectSigningKeyStoreFile(deployment.getRequestObjectSigningKeyStoreFile().replace(PROTOCOL_CLASSPATH, Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath())); } - if (!deployment.getRequestSignatureAlgorithm().equals(NONE) && deployment.getRequestObjectSigningKeystoreFile() == null){ + if (!deployment.getRequestObjectSignatureAlgorithm().equals(NONE) && deployment.getRequestObjectSigningKeyStoreFile() == null){ throw log.invalidKeyStoreConfiguration(); } else { - return new JWTSigningUtils().loadKeyPairFromKeyStore(deployment.getRequestObjectSigningKeystoreFile(), + return JWTSigningUtils.loadKeyPairFromKeyStore(deployment.getRequestObjectSigningKeyStoreFile(), deployment.getRequestObjectSigningKeyStorePassword(), deployment.getRequestObjectSigningKeyPassword(), - deployment.getRequestObjectSigningKeyAlias(), deployment.getRequestObjectSigningKeystoreType()); + deployment.getRequestObjectSigningKeyAlias(), deployment.getRequestObjectSigningKeyStoreType()); } } @@ -566,52 +571,47 @@ public JsonWebSignature signRequest(JwtClaims jwtClaims) throws IOException, Jos JsonWebSignature jsonWebSignature = new JsonWebSignature(); jsonWebSignature.setPayload(jwtClaims.toJson()); - if (!deployment.getRequestObjectSigningAlgValuesSupported().contains(deployment.getRequestSignatureAlgorithm())) { + if (!deployment.getRequestObjectSigningAlgValuesSupported().contains(deployment.getRequestObjectSignatureAlgorithm())) { throw log.invalidRequestObjectSignatureAlgorithm(); } else { - if (deployment.getRequestSignatureAlgorithm().contains(NONE)) { //unsigned + if (deployment.getRequestObjectSignatureAlgorithm().contains(NONE)) { //unsigned jsonWebSignature.setAlgorithmConstraints(AlgorithmConstraints.NO_CONSTRAINTS); jsonWebSignature.setAlgorithmHeaderValue(NONE); - } else if (deployment.getRequestSignatureAlgorithm().contains(HMAC_SHA256) - || deployment.getRequestSignatureAlgorithm().contains(HMAC_SHA384) - || deployment.getRequestSignatureAlgorithm().contains(HMAC_SHA512)) { //signed with symmetric key - jsonWebSignature.setAlgorithmHeaderValue(deployment.getRequestSignatureAlgorithm()); + } else if (deployment.getRequestObjectSignatureAlgorithm().contains(HMAC_SHA256) + || deployment.getRequestObjectSignatureAlgorithm().contains(HMAC_SHA384) + || deployment.getRequestObjectSignatureAlgorithm().contains(HMAC_SHA512)) { //signed with symmetric key + jsonWebSignature.setAlgorithmHeaderValue(deployment.getRequestObjectSignatureAlgorithm()); Key key = new HmacKey(deployment.getResourceCredentials().get("secret").toString().getBytes(StandardCharsets.UTF_8)); //the client secret is a shared secret between the server and the client jsonWebSignature.setDoKeyValidation(false); //skips validation so that size of the secret does not matter jsonWebSignature.setKey(key); } else { //signed with asymmetric key - KeyPair keyPair = getkeyPair(); + KeyPair keyPair = getkeyPair(deployment); jsonWebSignature.setKey(keyPair.getPrivate()); - jsonWebSignature.setAlgorithmHeaderValue(deployment.getRequestSignatureAlgorithm()); + jsonWebSignature.setAlgorithmHeaderValue(deployment.getRequestObjectSignatureAlgorithm()); } jsonWebSignature.sign(); return jsonWebSignature; } } - private JsonWebEncryption encryptRequest(JsonWebSignature signedRequest) throws IOException, JoseException { - if (!deployment.getRequestObjectEncryptionAlgValuesSupported().contains(deployment.getRequestEncryptAlgorithm())) { + private JsonWebEncryption encryptRequest(JsonWebSignature signedRequest) throws JoseException { + if (!deployment.getRequestObjectEncryptionAlgValuesSupported().contains(deployment.getRequestObjectEncryptAlgorithm())) { throw log.invalidRequestObjectEncryptionAlgorithm(); - } else if (!deployment.getRequestObjectContentEncryptionMethodsSupported().contains(deployment.getRequestContentEncryptionMethod())) { + } else if (!deployment.getRequestObjectContentEncryptionMethodsSupported().contains(deployment.getRequestObjectContentEncryptionMethod())) { throw log.invalidRequestObjectContentEncryptionAlgorithm(); } else { JsonWebEncryption jsonEncryption = new JsonWebEncryption(); jsonEncryption.setPayload(signedRequest.getCompactSerialization()); - jsonEncryption.setAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, deployment.getRequestEncryptAlgorithm(), deployment.getRequestContentEncryptionMethod())); - jsonEncryption.setAlgorithmHeaderValue(deployment.getRequestEncryptAlgorithm()); - jsonEncryption.setEncryptionMethodHeaderParameter(deployment.getRequestContentEncryptionMethod()); - JWK[] jwkList = new JWKPublicKeySetExtractor().extractPublicKeySet(deployment).getKeys(); - for (JWK jwk : jwkList) { - if (jwk.getPublicKeyUse().equals("enc")) { //JWTs are to be encrypted with public keys shared by the OpenID provider and decrypted by the private key they hold - jsonEncryption.setKey(new JWKParser(jwk).toPublicKey()); - break; - } - } + jsonEncryption.setAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, deployment.getRequestObjectEncryptAlgorithm(), deployment.getRequestObjectContentEncryptionMethod())); + jsonEncryption.setAlgorithmHeaderValue(deployment.getRequestObjectEncryptAlgorithm()); + jsonEncryption.setEncryptionMethodHeaderParameter(deployment.getRequestObjectContentEncryptionMethod()); + PublicKey encPublicKey = deployment.getEncryptionPublicKeyLocator().getPublicKey("enc", deployment); + jsonEncryption.setKey(encPublicKey); return jsonEncryption; } } - private String getRequestUri(String request) throws IOException, InvalidJwtException { + private String getRequestUri(String request) throws OidcException { HttpPost parRequest = new HttpPost(deployment.getPushedAuthorizationRequestEndpoint()); List formParams = new ArrayList(); formParams.add(new BasicNameValuePair(REQUEST, request)); @@ -620,21 +620,30 @@ private String getRequestUri(String request) throws IOException, InvalidJwtExcep UrlEncodedFormEntity form = new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8); parRequest.setEntity(form); - HttpResponse response = deployment.getClient().execute(parRequest); + + HttpResponse response; + try { + response = deployment.getClient().execute(parRequest); + } catch (Exception e) { + throw log.failedToConnectToOidcProvider(e); + } if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) { EntityUtils.consumeQuietly(response.getEntity()); throw log.unexpectedResponseCodeFromOidcProvider(response.getStatusLine().getStatusCode()); } - InputStream inputStream = response.getEntity().getContent(); - StringBuilder textBuilder = new StringBuilder(); - try (Reader reader = new BufferedReader(new InputStreamReader - (inputStream, StandardCharsets.UTF_8))) { + try { + InputStream inputStream = response.getEntity().getContent(); + StringBuilder textBuilder = new StringBuilder(); + Reader reader = new BufferedReader(new InputStreamReader + (inputStream, StandardCharsets.UTF_8)); int c = 0; while ((c = reader.read()) != -1) { textBuilder.append((char) c); } + JwtClaims jwt = JwtClaims.parse(textBuilder.toString()); + return jwt.getClaimValueAsString(REQUEST_URI); + } catch (IOException | InvalidJwtException e) { + throw log.failedToDecodeRequestUri(e); } - JwtClaims jwt = JwtClaims.parse(textBuilder.toString()); - return jwt.getClaimValueAsString(REQUEST_URI); } } diff --git a/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java b/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java index 831a921635..6aa511cce8 100644 --- a/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java +++ b/http/oidc/src/test/java/org/wildfly/security/http/oidc/KeycloakConfiguration.java @@ -75,6 +75,7 @@ public class KeycloakConfiguration { public static final String A192CBC_HS384 = "A192CBC-HS384"; public static final String A256CBC_HS512 = "A256CBC-HS512"; public static CAGenerationTool caGenerationTool = null; + public X509Certificate caCertificate = null; // the users below are for multi-tenancy tests specifically public static final String TENANT1_USER = "tenant1_user"; @@ -227,11 +228,6 @@ private static ClientRepresentation createWebAppClient(String clientId, String c return createWebAppClient(clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, null, multiTenancyApp); } - private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort, - String clientApp, boolean directAccessGrantEnabled, String allowedOrigin) throws Exception { - return createWebAppClient(clientId, clientSecret, clientHostName, clientPort, clientApp, directAccessGrantEnabled, allowedOrigin, false); - } - private static ClientRepresentation createWebAppClient(String clientId, String clientSecret, String clientHostName, int clientPort, String clientApp, boolean directAccessGrantEnabled, String allowedOrigin, boolean multiTenancyApp) throws Exception { ClientRepresentation client = new ClientRepresentation(); @@ -250,16 +246,18 @@ private static ClientRepresentation createWebAppClient(String clientId, String c if (allowedOrigin != null) { client.setWebOrigins(Collections.singletonList(allowedOrigin)); } - OIDCAdvancedConfigWrapper oidcAdvancedConfigWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(client); - oidcAdvancedConfigWrapper.setUseJwksUrl(false); - KEYSTORE_CLASSPATH = Objects.requireNonNull(KeycloakConfiguration.class.getClassLoader().getResource("")).getPath(); - caGenerationTool = CAGenerationTool.builder() - .setBaseDir(KEYSTORE_CLASSPATH) - .setRequestIdentities(CAGenerationTool.Identity.values()) // Create all identities. - .build(); - X500Principal principal = new X500Principal("OU=Elytron, O=Elytron, C=UK, ST=Elytron, CN=OcspResponder"); - X509Certificate rsaCert = caGenerationTool.createIdentity(KEYSTORE_ALIAS, principal, RSA_KEYSTORE_FILE_NAME, CAGenerationTool.Identity.CA); - client.getAttributes().put("jwt.credential.certificate", Base64.getEncoder().encodeToString(rsaCert.getEncoded())); + if (KeycloakConfiguration.class.getClassLoader().getResource(RSA_KEYSTORE_FILE_NAME) == null) { + OIDCAdvancedConfigWrapper oidcAdvancedConfigWrapper = OIDCAdvancedConfigWrapper.fromClientRepresentation(client); + oidcAdvancedConfigWrapper.setUseJwksUrl(false); + KEYSTORE_CLASSPATH = Objects.requireNonNull(KeycloakConfiguration.class.getClassLoader().getResource("")).getPath(); + caGenerationTool = CAGenerationTool.builder() + .setBaseDir(KEYSTORE_CLASSPATH) + .setRequestIdentities(CAGenerationTool.Identity.values()) // Create all identities. + .build(); + X500Principal principal = new X500Principal("OU=Elytron, O=Elytron, C=UK, ST=Elytron, CN=OcspResponder"); + X509Certificate rsaCert = caGenerationTool.createIdentity(KEYSTORE_ALIAS, principal, RSA_KEYSTORE_FILE_NAME, CAGenerationTool.Identity.CA); + client.getAttributes().put("jwt.credential.certificate", Base64.getEncoder().encodeToString(rsaCert.getEncoded())); + } return client; } diff --git a/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java b/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java index a5ff5561e2..ed85b83618 100644 --- a/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java +++ b/http/oidc/src/test/java/org/wildfly/security/http/oidc/OidcTest.java @@ -58,7 +58,10 @@ import java.util.HashMap; import java.util.Map; +import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.TextPage; +import io.restassured.RestAssured; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.QueueDispatcher; import org.apache.http.HttpStatus; @@ -67,12 +70,6 @@ import org.junit.Test; import org.wildfly.security.http.HttpServerAuthenticationMechanism; -import com.gargoylesoftware.htmlunit.TextPage; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.html.HtmlPage; - -import io.restassured.RestAssured; - /** * Tests for the OpenID Connect authentication mechanism. * @@ -280,7 +277,7 @@ public void testOpenIDWithPsSignedAndRsaEncryptedRequest() throws Exception { } @Test - public void testOpenIDWithInvalidSignAlgorithm() throws Exception { + public void testOpenIDWithInvalidSigningAlgorithm() throws Exception { //RSNULL is a valid signature algorithm, but not one of the ones supported by keycloak performAuthentication(getOidcConfigurationInputStreamWithRequestParameter(REQUEST_TYPE_REQUEST.getValue(), "RSNULL", RSA_OAEP_256, A256CBC_HS512, KEYSTORE_CLASSPATH + KeycloakConfiguration.RSA_KEYSTORE_FILE_NAME, KeycloakConfiguration.KEYSTORE_ALIAS, PKCS12_KEYSTORE_TYPE), KeycloakConfiguration.ALICE, KeycloakConfiguration.ALICE_PASSWORD, true, HttpStatus.SC_MOVED_TEMPORARILY, getClientUrl(), CLIENT_PAGE_TEXT, true); @@ -623,7 +620,7 @@ private void performAuthentication(InputStream oidcConfig, String username, Stri mechanism.evaluateRequest(request); } catch (Exception e) { if (checkInvalidRequestAlgorithm) { - assertTrue(e.getMessage().contains("java.io.IOException: ELY23058")); + assertTrue(e.getMessage().contains("ELY23058: The request object signature algorithm specified is not supported by the OpenID Provider.")); return; //Expected to get an exception and ignore the rest } else { throw e;