[java-oidc-common] branch main updated: Generalize JWT signature checking, deprecate original methods.

Scott Cantor cantor.2 at osu.edu
Wed Jan 26 19:44:53 UTC 2022


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

scantor pushed a commit to branch main
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=87c7a056346cb91d58c891fa6e533cd1d86070fb

The following commit(s) were added to refs/heads/main by this push:
     new 87c7a05  Generalize JWT signature checking, deprecate original methods.
87c7a05 is described below

commit 87c7a056346cb91d58c891fa6e533cd1d86070fb
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jan 26 14:44:50 2022 -0500

    Generalize JWT signature checking, deprecate original methods.
---
 .../security/impl/JWTSignatureValidationUtil.java  | 79 +++++++++++++++-------
 .../impl/JWTSignatureValidationUtilTest.java       | 69 ++++++++++++++++---
 2 files changed, 112 insertions(+), 36 deletions(-)

diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtil.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtil.java
index ea5ff4b..4aedef5 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtil.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtil.java
@@ -68,7 +68,10 @@ public final class JWTSignatureValidationUtil {
      * @param signedJwt The signed JWT to be validated.
      * @param invalidJwtEventId The event identifier describing the invalid JWT.
      * @return an event ID on failure, or {@literal null} if validation succeeds.
+     * 
+     * @deprecated
      */
+    @Deprecated(since="1.2.0", forRemoval=true)
     @Nullable public static String validateSignature(@Nullable final SecurityParametersContext secParamCtx, 
             @Nonnull final SignedJWT signedJwt, @Nullable final String invalidJwtEventId) {
         
@@ -88,7 +91,7 @@ public final class JWTSignatureValidationUtil {
             return EventIds.INVALID_SEC_CFG;            
         }        
         
-        return validateSignature(signatureValidationParameters.getValidationCredentials(), 
+        return validateSignatureEx(signatureValidationParameters.getValidationCredentials(), 
                 signedJwt, invalidJwtEventId);
     }
 
@@ -97,39 +100,65 @@ public final class JWTSignatureValidationUtil {
      * any reason, including insufficient prequisities in the context, an event identifier is returned. Successful
      * validation produces null result.
      * 
-     * @param credentials The list of {@link JWKCredential credentials} to use for signature validation.
+     * @param credentials The list of {@link JWKCredential}s to use for signature validation.
      * @param signedJwt The signed JWT to be validated.
      * @param invalidJwtEventId The event identifier describing the invalid JWT.
      * @return an event ID on failure, or {@literal null} if validation succeeds.
+     * 
+     * @deprecated
      */
+    @Deprecated(since="1.2.0", forRemoval=true)
     public static String validateSignature(@Nonnull final List<JWKCredential> credentials,
             @Nonnull final SignedJWT signedJwt, @Nullable final String invalidJwtEventId) {
+        return validateSignatureEx(credentials, signedJwt, invalidJwtEventId);
+    }
+
+
+    /**
+     * Validates the signature of the given JWS against the given list of credentials. If the validation fails for
+     * any reason, including insufficient prequisities in the context, an event identifier is returned. Successful
+     * validation produces null result.
+     * 
+     * @param credentials The list of {@link Credential}s to use for signature validation.
+     * @param signedJwt The signed JWT to be validated.
+     * @param invalidJwtEventId The event identifier describing the invalid JWT.
+     * @return an event ID on failure, or {@literal null} if validation succeeds.
+     */
+    public static String validateSignatureEx(@Nonnull final List<? extends Credential> credentials,
+            @Nonnull final SignedJWT signedJwt, @Nullable final String invalidJwtEventId) {
         
         final Algorithm algorithm = signedJwt.getHeader().getAlgorithm();
-        final Iterator<?> it = credentials.iterator();
+        final Iterator<? extends Credential> it = credentials.iterator();
         while (it.hasNext()) {
-            final JWKCredential credential = (JWKCredential) it.next();
-            if (!algorithm.equals(credential.getAlgorithm())) {
-                log.debug("Credential alg {} not matching jwt header alg {}", credential.getAlgorithm().getName(),
-                        algorithm.getName());
-            } else {
-                try {
-                    final JWSVerifier verifier = initializeVerifier(algorithm, credential);
-                    if (verifier == null) {
-                        log.warn("No verifier for given JWT for alg {}", algorithm.getName());
-                        return EventIds.INVALID_SEC_CFG;
-                    }
-                    if (signedJwt.verify(verifier)) {
-                        log.debug("JWT {} verified using algorithm {} {}", signedJwt.serialize(),
+            final Credential credential = it.next();
+            if (credential instanceof JWKCredential && !algorithm.equals(((JWKCredential) credential).getAlgorithm())) {
+                log.debug("Credential alg {} not matching jwt header alg {}",
+                        ((JWKCredential) credential).getAlgorithm().getName(), algorithm.getName());
+                continue;
+            }
+            
+            try {
+                final JWSVerifier verifier = initializeVerifier(algorithm, credential);
+                if (verifier == null) {
+                    log.debug("No verifier for given JWT and Credential pair for alg {}", algorithm.getName());
+                    continue;
+                }
+                if (signedJwt.verify(verifier)) {
+                    if (log.isDebugEnabled()) {
+                        final String kid = credential instanceof JWKCredential ?
+                                ((JWKCredential) credential).getKid() : null;
+                        log.debug("JWT {} verified using algorithm {}{}", signedJwt.serialize(),
                                 algorithm.getName(), 
-                                credential.getKid() != null ? "and key " + credential.getKid() : "");
-                        return null;
+                                kid != null ? " and key " + kid : "");
                     }
-                    log.debug("Unable to validate given JWT with credential, picking next key");
-                } catch (final JOSEException | IllegalStateException e) {
-                    log.warn("Exception caught when validating given JWT {}", 
-                            credential.getKid() != null ? "with credential " + credential.getKid() : "", e);
+                    return null;
                 }
+                log.debug("Unable to validate given JWT with credential, picking next key");
+            } catch (final JOSEException | IllegalStateException e) {
+                final String kid = credential instanceof JWKCredential ?
+                        ((JWKCredential) credential).getKid() : null;
+                log.warn("Exception caught when validating given JWT{}",
+                        kid != null ? " with credential " + kid : "", e);
             }
         }
         log.warn("Unable to validate given JWT with any of the credentials");
@@ -145,13 +174,13 @@ public final class JWTSignatureValidationUtil {
      */
     private static JWSVerifier initializeVerifier(final Algorithm algorithm, final Credential credential)
             throws JOSEException {
-        if (JWSAlgorithm.Family.HMAC_SHA.contains(algorithm)) {
+        if (JWSAlgorithm.Family.HMAC_SHA.contains(algorithm) && credential.getSecretKey() != null) {
             return new MACVerifier(credential.getSecretKey());
         }
-        if (JWSAlgorithm.Family.RSA.contains(algorithm)) {
+        if (JWSAlgorithm.Family.RSA.contains(algorithm) && credential.getPublicKey() instanceof RSAPublicKey) {
             return new RSASSAVerifier((RSAPublicKey) credential.getPublicKey());
         }
-        if (JWSAlgorithm.Family.EC.contains(algorithm)) {
+        if (JWSAlgorithm.Family.EC.contains(algorithm) && credential.getPublicKey() instanceof ECPublicKey) {
             return new ECDSAVerifier((ECPublicKey) credential.getPublicKey());
         }
         return null;
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtilTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtilTest.java
index f645980..f4d7ed6 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtilTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/JWTSignatureValidationUtilTest.java
@@ -30,6 +30,8 @@ import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.opensaml.profile.action.EventIds;
+import org.opensaml.security.credential.BasicCredential;
+import org.opensaml.security.credential.Credential;
 import org.opensaml.xmlsec.SignatureSigningParameters;
 import org.opensaml.xmlsec.context.SecurityParametersContext;
 import org.testng.Assert;
@@ -57,16 +59,24 @@ public class JWTSignatureValidationUtilTest {
     final String invalidJwtEventId = "invalid_jwt";
 
     BasicJWKCredential rsaCredential;
-    
     BasicJWKCredential ecCredential;
-    
     BasicJWKCredential sharedCredential;
+
+    BasicCredential rsaCredentialBasic;
+    BasicCredential ecCredentialBasic;
+
+    List<Credential> credentialList;
     
     @BeforeClass
     public void init() throws NoSuchAlgorithmException {
-        rsaCredential = createKeyPairCredential("RSA", "RS256", "mockRSAKey", 2048);
-        ecCredential = createKeyPairCredential("EC", "ES256", "mockECKey", 256);
+        rsaCredential = createJWKKeyPairCredential("RSA", "RS256", "mockRSAKey", 2048);
+        ecCredential = createJWKKeyPairCredential("EC", "ES256", "mockECKey", 256);
         sharedCredential = createSharedSecretCredential("HS256", "mockSharedSecret");
+        
+        rsaCredentialBasic = createBasicKeyPairCredential("RSA", 2048);
+        ecCredentialBasic = createBasicKeyPairCredential("EC", 256);
+        
+        credentialList = List.of(rsaCredential, ecCredential, sharedCredential, rsaCredentialBasic, ecCredentialBasic);
     }
     
     @Test
@@ -97,7 +107,6 @@ public class JWTSignatureValidationUtilTest {
                 EventIds.INVALID_SEC_CFG);
     }
    
-
     @Test
     public void validateSignature_shouldReturnNullWhenValidRSASignature()
             throws JOSEException, ParseException, NoSuchAlgorithmException {
@@ -105,16 +114,31 @@ public class JWTSignatureValidationUtilTest {
         Assert.assertNull(JWTSignatureValidationUtil.validateSignature(secCtx, signRSA(rsaCredential.getPrivateKey()),
                 invalidJwtEventId));
     }
-    
+
+    @Test
+    public void validateSignature_shouldReturnNullWhenValidRSASignatureEx()
+            throws JOSEException, ParseException, NoSuchAlgorithmException {
+        Assert.assertNull(JWTSignatureValidationUtil.validateSignatureEx(credentialList,
+                signRSA(rsaCredentialBasic.getPrivateKey()), invalidJwtEventId));
+    }
+
     @Test
     public void validateSignature_shouldReturnEventIdWhenInvalidRSASignature()
             throws NoSuchAlgorithmException, JOSEException, ParseException {
-        final BasicJWKCredential anotherRsaCredential = createKeyPairCredential("RSA", "RS256", "mockRSAKey2", 2048);
+        final BasicJWKCredential anotherRsaCredential = createJWKKeyPairCredential("RSA", "RS256", "mockRSAKey2", 2048);
         final SecurityParametersContext secCtx = initSecurityParamsContext("RS256", rsaCredential);
         Assert.assertEquals(JWTSignatureValidationUtil.validateSignature(secCtx,
                 signRSA(anotherRsaCredential.getPrivateKey()), invalidJwtEventId), invalidJwtEventId);        
     }
-    
+
+    @Test
+    public void validateSignature_shouldReturnEventIdWhenInvalidRSASignatureEx()
+            throws NoSuchAlgorithmException, JOSEException, ParseException {
+        final BasicJWKCredential anotherRsaCredential = createJWKKeyPairCredential("RSA", "RS256", "mockRSAKey2", 2048);
+        Assert.assertEquals(JWTSignatureValidationUtil.validateSignatureEx(credentialList,
+                signRSA(anotherRsaCredential.getPrivateKey()), invalidJwtEventId), invalidJwtEventId);        
+    }
+
     @Test
     public void validateSignature_shouldReturnNullWhenValidECSignature()
             throws JOSEException, ParseException, NoSuchAlgorithmException {
@@ -123,15 +147,30 @@ public class JWTSignatureValidationUtilTest {
                 signEC((ECPrivateKey) ecCredential.getPrivateKey()), invalidJwtEventId));
     }
 
+    @Test
+    public void validateSignature_shouldReturnNullWhenValidECSignatureEx()
+            throws JOSEException, ParseException, NoSuchAlgorithmException {
+        Assert.assertNull(JWTSignatureValidationUtil.validateSignatureEx(credentialList,
+                signEC((ECPrivateKey) ecCredentialBasic.getPrivateKey()), invalidJwtEventId));
+    }
+
     @Test
     public void validateSignature_shouldReturnEventIdWhenInvalidECSignature()
             throws JOSEException, ParseException, NoSuchAlgorithmException {
-        final BasicJWKCredential anotherEcCredential = createKeyPairCredential("EC", "ES256", "mockECKey2", 256);
+        final BasicJWKCredential anotherEcCredential = createJWKKeyPairCredential("EC", "ES256", "mockECKey2", 256);
         SecurityParametersContext secCtx = initSecurityParamsContext("ES256", ecCredential);
         Assert.assertEquals(JWTSignatureValidationUtil.validateSignature(secCtx,
                 signEC((ECPrivateKey) anotherEcCredential.getPrivateKey()), invalidJwtEventId), invalidJwtEventId);        
     }
 
+    @Test
+    public void validateSignature_shouldReturnEventIdWhenInvalidECSignatureEx()
+            throws JOSEException, ParseException, NoSuchAlgorithmException {
+        final BasicJWKCredential anotherEcCredential = createJWKKeyPairCredential("EC", "ES256", "mockECKey2", 256);
+        Assert.assertEquals(JWTSignatureValidationUtil.validateSignatureEx(credentialList,
+                signEC((ECPrivateKey) anotherEcCredential.getPrivateKey()), invalidJwtEventId), invalidJwtEventId);        
+    }
+
     @Test
     public void validateSignature_shouldReturnNullWhenValidMACSignature() throws JOSEException, ParseException {
         SecurityParametersContext secCtx = initSecurityParamsContext("HS256", sharedCredential);
@@ -180,7 +219,7 @@ public class JWTSignatureValidationUtilTest {
         return SignedJWT.parse(jwsObject.serialize());
     }
 
-    protected BasicJWKCredential createKeyPairCredential(final String keyPairAlgorithm, final String jwsAlgorithm,
+    protected BasicJWKCredential createJWKKeyPairCredential(final String keyPairAlgorithm, final String jwsAlgorithm,
             final String kid, final int keysize) throws NoSuchAlgorithmException {   
         final KeyPairGenerator generator = KeyPairGenerator.getInstance(keyPairAlgorithm);
         generator.initialize(keysize);
@@ -193,7 +232,15 @@ public class JWTSignatureValidationUtilTest {
 
         return credential;
     }
-    
+
+    protected BasicCredential createBasicKeyPairCredential(final String keyPairAlgorithm, final int keysize)
+            throws NoSuchAlgorithmException {   
+        final KeyPairGenerator generator = KeyPairGenerator.getInstance(keyPairAlgorithm);
+        generator.initialize(keysize);
+        final KeyPair keyPair = generator.generateKeyPair();
+        return new BasicCredential(keyPair.getPublic(), keyPair.getPrivate());
+    }
+
     protected BasicJWKCredential createSharedSecretCredential(final String jwsAlgorithm, final String kid) {
         final BasicJWKCredential credential = new BasicJWKCredential();
         credential.setAlgorithm(JWSAlgorithm.parse(jwsAlgorithm));

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


More information about the commits mailing list