[java-idp-plugin-oidc-rp] branch main updated: Update client authentication handler to use JWSTokenSigner in commons

Phil Smart philip.smart at jisc.ac.uk
Tue Oct 3 11:12:17 UTC 2023


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=8493d99d354fd7f8ac565f12f22601550423bc1a

The following commit(s) were added to refs/heads/main by this push:
     new 8493d99  Update client authentication handler to use JWSTokenSigner in commons
8493d99 is described below

commit 8493d99d354fd7f8ac565f12f22601550423bc1a
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Tue Oct 3 12:12:10 2023 +0100

    Update client authentication handler to use JWSTokenSigner in commons
---
 ...izeOAuth2ClientAuthenticationMethodHandler.java | 170 ++++-----------------
 1 file changed, 31 insertions(+), 139 deletions(-)

diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java
index 8352120..66e68b0 100644
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/InitializeOAuth2ClientAuthenticationMethodHandler.java
@@ -14,8 +14,6 @@
 
 package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
 
-import java.nio.charset.StandardCharsets;
-import java.security.interfaces.ECPrivateKey;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Date;
@@ -23,7 +21,6 @@ import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import javax.crypto.SecretKey;
 
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.context.navigate.ChildContextLookup;
@@ -32,19 +29,11 @@ import org.opensaml.messaging.handler.MessageHandlerException;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.profile.context.navigate.ParentProfileRequestContextLookup;
-import org.opensaml.security.credential.Credential;
 import org.slf4j.Logger;
 
 import com.nimbusds.jose.Algorithm;
-import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JOSEObjectType;
 import com.nimbusds.jose.JWSAlgorithm;
-import com.nimbusds.jose.JWSHeader;
-import com.nimbusds.jose.JWSObject.State;
-import com.nimbusds.jose.JWSSigner;
-import com.nimbusds.jose.crypto.ECDSASigner;
-import com.nimbusds.jose.crypto.MACSigner;
-import com.nimbusds.jose.crypto.RSASSASigner;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.SignedJWT;
 import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
@@ -62,9 +51,9 @@ import net.shibboleth.oidc.authn.context.OAuth2ClientAuthenticationContext;
 import net.shibboleth.oidc.metadata.context.OIDCProviderMetadataContext;
 import net.shibboleth.oidc.profile.config.OIDCAuthenticationRelyingPartyProfileConfiguration;
 import net.shibboleth.oidc.profile.messaging.context.OIDCPeerEntityContext;
-import net.shibboleth.oidc.security.CredentialConversionUtil;
 import net.shibboleth.oidc.security.credential.ClientSecretCredential;
-import net.shibboleth.oidc.security.credential.JWKCredential;
+import net.shibboleth.oidc.security.impl.JWSTokenSigner;
+import net.shibboleth.oidc.security.jose.SignatureException;
 import net.shibboleth.oidc.security.jose.SignatureSigningParameters;
 import net.shibboleth.oidc.security.jose.context.SecurityParametersContext;
 import net.shibboleth.profile.context.RelyingPartyContext;
@@ -311,7 +300,7 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
         }
         
         if (clientAuthentication == null) {
-            throw new MessageHandlerException("Client authentication could be constructed");
+            throw new MessageHandlerException("Client authentication could not be constructed");
         }
 
         oauth2ClientAuthenticationContext.setClientAuthentication(clientAuthentication);   
@@ -331,17 +320,18 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
         
         final SecurityParametersContext bearerSecurityParams = jwtBearerClientAuthSecurityParameters;        
         if (bearerSecurityParams == null || bearerSecurityParams.getSignatureSigningParameters() == null) {
-            throw new MessageHandlerException("Missing security parameters needed to build client_secret_jwt");
+            throw new MessageHandlerException("Missing security parameters needed to sign client_secret_jwt");
         }
         
-        final SignatureSigningParameters signatureSigningParameters = bearerSecurityParams.getSignatureSigningParameters();
+        final SignatureSigningParameters signatureSigningParameters = 
+                bearerSecurityParams.getSignatureSigningParameters();
         assert signatureSigningParameters != null;
         
         if (signatureSigningParameters.getSignatureAlgorithm() == null || 
                 signatureSigningParameters.getSigningCredential() == null) {
-            throw new MessageHandlerException("Missing credential needed to build client_secret_jwt");
+            throw new MessageHandlerException("Missing credential needed to sign client_secret_jwt");
         }
-        final Algorithm jwsAlgorithm = resolveAlgorithm(signatureSigningParameters);
+        final Algorithm jwsAlgorithm = new JWSAlgorithm(signatureSigningParameters.getSignatureAlgorithm());
         if (!JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
             throw new MessageHandlerException("Trying to construct client_secret_jwt using the wrong algorithm: "
                     + jwsAlgorithm);
@@ -359,16 +349,17 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
         
         final SecurityParametersContext bearerSecurityParams = jwtBearerClientAuthSecurityParameters;        
         if (bearerSecurityParams == null || bearerSecurityParams.getSignatureSigningParameters() == null) {
-            throw new MessageHandlerException("Missing security parameters needed to private_key_jwt");
+            throw new MessageHandlerException("Missing security parameters needed to sign private_key_jwt");
         }
         
-        final SignatureSigningParameters signatureSigningParameters = bearerSecurityParams.getSignatureSigningParameters();
+        final SignatureSigningParameters signatureSigningParameters = 
+                bearerSecurityParams.getSignatureSigningParameters();
         assert signatureSigningParameters != null;
         
         if (signatureSigningParameters.getSigningCredential() == null) {
-            throw new MessageHandlerException("Missing credential needed to build private_key_jwt");
+            throw new MessageHandlerException("Missing credential needed to sign private_key_jwt");
         }
-        final Algorithm jwsAlgorithm = resolveAlgorithm(signatureSigningParameters);
+        final Algorithm jwsAlgorithm = new JWSAlgorithm(signatureSigningParameters.getSignatureAlgorithm());
         if (!JWSAlgorithm.Family.SIGNATURE.contains(jwsAlgorithm)) {
             throw new MessageHandlerException("Trying to construct private_key_jwt using the wrong algorithm: "
                     + jwsAlgorithm);
@@ -398,136 +389,37 @@ public class InitializeOAuth2ClientAuthenticationMethodHandler extends AbstractM
      * the correct 'alg' and credential existing in the security context ahead of time for the correct SignedJWT to be
      * returned e.g. for either client_secret_jwt or private_key_jwt.
      * 
-     * @return a signed JWT bearer token, or {@code null} if there was an error during construction
+     * @return a signed JWT bearer token, or throws an exception if there was an error during construction
+     * 
+     * @throws MessageHandlerException on error constructing the JWT
      */
-    @Nullable private SignedJWT buildClientAuthenticationJwt() {
+    @Nonnull private SignedJWT buildClientAuthenticationJwt() throws MessageHandlerException {
         
         final SecurityParametersContext bearerSecurityParams = jwtBearerClientAuthSecurityParameters; 
         if (bearerSecurityParams == null) {
-            log.error("{} Requested client_secret_jwt client authentication, but signing parameters context "
-                    + "could not be found",getLogPrefix());
-            return null;
+            throw new MessageHandlerException("Requested client_secret_jwt client authentication, but "
+                    + "signing parameters context could not be found");
         }
         final SignatureSigningParameters signingParams = bearerSecurityParams.getSignatureSigningParameters();
         if (signingParams == null) {
-            log.error("{} Requested client_secret_jwt client authentication, but signing parameters "
-                    + "could not be found",getLogPrefix());
-            return null;
+            throw new MessageHandlerException("Requested client_secret_jwt client authentication, but "
+                    + "signing parameters could not be found");
         }
         final JWTClaimsSet claims = buildClientAuthenticationJwtClaims();
-        final SignedJWT signed = signClaims(claims, signingParams);
-        if (signed == null) {
-            log.trace("Could not construct client_secret_jwt client authentication");
-            return null;
-        }
-        return signed;
-    }
-
-    /**
-     * Sign the given JWT claims set using the signing parameters from the context.
-     * 
-     * @param jwtClaimSetToSign the claims to sign
-     * @param signingParams the signing parameters required to sign the JWT
-     * 
-     * @return a signed JWT or {@code null} if an error occurs.
-     */
-    @Nullable private SignedJWT signClaims(@Nonnull final JWTClaimsSet jwtClaimSetToSign, 
-            @Nonnull final SignatureSigningParameters signingParams) {
+        
         try {
-            SignedJWT jwt = null; 
-            final Credential credential = signingParams.getSigningCredential();
-            if (credential == null) {
-                log.error("{} JWT Bearer Token could not be signed, no signing credential found", getLogPrefix());
-                return null; 
-            }
-            final Algorithm jwsAlgorithm = resolveAlgorithm(signingParams);
-            if (jwsAlgorithm == null) {
-                log.error("{} JWT Bearer Token could not be signed, no signing algorithm found", getLogPrefix());
-                return null; 
-            }
-            final JWSSigner signer = getSigner(jwsAlgorithm, credential);
-            final JWSHeader.Builder headerBuilder = new JWSHeader.Builder(new JWSAlgorithm(jwsAlgorithm.getName()))
-                    .keyID(CredentialConversionUtil.resolveKid(credential));
-            headerBuilder.type(JOSEObjectType.JWT);
-            jwt = new SignedJWT(headerBuilder.build(), jwtClaimSetToSign);
-            jwt.sign(signer);
+            final JWSTokenSigner signer = new JWSTokenSigner(signingParams);
+            final SignedJWT signed = signer.sign(claims, JOSEObjectType.JWT.getType());    
             if (log.isDebugEnabled() && !log.isTraceEnabled()) {
-                log.debug("{} Signed JWT Bearer Token for client authentication using kid '{}'", getLogPrefix(), 
-                        CredentialConversionUtil.resolveKid(credential));
+                log.debug("{} Signed JWT Bearer Token for client authentication'", getLogPrefix());
             } else if (log.isTraceEnabled()) {
-                log.trace("{} Signed JWT Bearer Token for client authentication using kid '{}': {}", getLogPrefix(), 
-                        CredentialConversionUtil.resolveKid(credential),jwt.serialize());
-            }            
-            
-            if (jwt.getState() != State.SIGNED) {
-                // Should not really happen, as JOSEException should be thrown
-                log.error("{} JWT Bearer Token for client authentication was not signed", getLogPrefix());
-                return null;
-            }
-            return jwt;
-            
-        } catch (final JOSEException e) {
-            log.error("{} Error signing claims set: {}", getLogPrefix(), e.getMessage());
-            return null;
-        }
-    }
-    
-    /**
-     * Returns correct implementation of signer based on algorithm type.
-     * 
-     * @param jwsAlgorithm JWS algorithm
-     * @param credential the credential to use
-     * @return signer for algorithm and private key
-     * @throws JOSEException if algorithm cannot be supported
-     */
-    private JWSSigner getSigner(@Nonnull final Algorithm jwsAlgorithm, 
-            @Nonnull final Credential credential) throws JOSEException {
-        if (JWSAlgorithm.Family.EC.contains(jwsAlgorithm)) {
-            return new ECDSASigner((ECPrivateKey) credential.getPrivateKey());
-        }
-        if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm)) {
-            return new RSASSASigner(credential.getPrivateKey());
-        }
-        if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
-            return new MACSigner(credential.getSecretKey());
-        }
-        throw new JOSEException("Unsupported algorithm " + jwsAlgorithm.getName());
-    }
-    
-    /**
-     * Resolves JWS algorithm from signature signing parameters.
-     * 
-     * @param params the signature signing parameters
-     * @return JWS algorithm
-     */
-    @Nullable protected JWSAlgorithm resolveAlgorithm(@Nonnull final SignatureSigningParameters params) {
-
-        if (params.getSignatureAlgorithm() == null) {
-            log.trace("{} Signature algorithm is null", getLogPrefix());
-            return null;
-        }
-        final JWSAlgorithm algorithm = new JWSAlgorithm(params.getSignatureAlgorithm());
-        final Credential credential = params.getSigningCredential();
-        if (credential instanceof final JWKCredential jwkCred && !algorithm.equals(jwkCred.getAlgorithm())) {
-            log.debug("{} Signature signing algorithm {} differs from JWK algorithm '{}'", getLogPrefix(),
-                    algorithm.getName(), jwkCred.getAlgorithm() != null ? 
-                            jwkCred.getAlgorithm() : "not specified");            
-        }
-        log.trace("{} Algorithm resolved {}", getLogPrefix(), algorithm.getName());
-        return algorithm;
-    }
-
-    /**
-     * Convert the encoded byte array representing the secret into a UTF-8 String.
-     * 
-     * @param key the key to convert
-     * @return the UTF-8 encoded string value of the secret. 
-     */
-    @Nullable private String convertSecretKeyToString(@Nullable final SecretKey key) {
-        if (key == null || key.getEncoded() == null) {
-            return null;
+                log.trace("{} Signed JWT Bearer Token for client authentication: {}", getLogPrefix()
+                        ,signed.serialize());
+            }  
+            return signed;
+        } catch (final SignatureException e) {
+            throw new MessageHandlerException(e);
         }
-        return new String(key.getEncoded(),StandardCharsets.UTF_8);
     }
     
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list