[java-oidc-common] 27/35: Relax algorithm check on credentials and JSON web keys

Phil Smart philip.smart at jisc.ac.uk
Tue Sep 20 14:19:27 UTC 2022


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

philsmart pushed a commit to branch dev/JCOMOIDC-41
in repository java-oidc-common.

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

commit d396c37562b20b5dba245664b1fea4e85ae1fa1c
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Aug 19 14:20:12 2022 +0100

    Relax algorithm check on credentials and JSON web keys
    
     - As 'alg' is optional in the JWK spec. the credential to algorithm
    matching must be relaxed to only use key type if 'alg' is not present.
     - As 'use' is optional in the JWK spec. the use type of UNSPECIFIED
    must also be allowed alongside ENCRYPTION
---
 .../impl/BasicJWTEncryptionParametersResolver.java | 37 ++++++++-
 ...oviderMetadataEncryptionParametersResolver.java | 96 ++++++++++++++++++++--
 .../BasicJWTEncryptionParametersResolverTest.java  | 93 +++++++++++++++++++--
 ...erMetadataEncryptionParametersResolverTest.java | 93 ++++++++++++++++++++-
 ...ider-resolver-remote-jwkset-response-no-alg.jwk |  4 +-
 ...-resolver-remote-jwkset-response-no-keyuse.jwk} |  5 +-
 ...olver-remote-jwkset-response-wrong-key-use.jwk} |  6 +-
 7 files changed, 301 insertions(+), 33 deletions(-)

diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java
index 93e1ce0..2f648fc 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolver.java
@@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory;
 import com.nimbusds.jose.Algorithm;
 import com.nimbusds.jose.EncryptionMethod;
 import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.jwk.KeyType;
 
 import net.shibboleth.oidc.security.JWTEncryptionConfiguration;
 import net.shibboleth.oidc.security.JWTEncryptionParameters;
@@ -423,7 +424,8 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
      * <li>The credential must be a {@link JWKCredential}</li>
      * <li>The credential must have a {@link UsageType} of {@link UsageType#ENCRYPTION} 
      * or {@link UsageType#UNSPECIFIED}</li>
-     * <li>The credentials algorithm must match to one of the input key transport algorithms</li>
+     * <li>If the credentials JWEAlgorithm is present, the credential's algorithm must match to the 
+     * input JWEAlgorithm. Else, the key's algorithm must match the JWE algorithm family.</li>
      * <li>The credential's key must match the keylength required by that algorithm</li>
      * </ol>
      * 
@@ -443,11 +445,41 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
                 .filter(JWKCredential.class::isInstance)
                 .filter(k -> UsageType.ENCRYPTION == k.getUsageType() ||  UsageType.UNSPECIFIED == k.getUsageType())
                 .map(JWKCredential.class::cast)
-                .filter(k -> algorithm.equals(k.getAlgorithm()))
+                .filter(k -> checkKeyWithAlgorithm(k, algorithm))
                 .filter(k -> checkKeyAlgorithmAndLength(k, algorithm.getName()))
                 .findFirst().orElse(null);
     }
     
+    /**
+     * Check the credential supports the algorithm specified. If the algorithm is present, check that is 
+     * identical to the algorithm supplied. If not, check key algorithm is compatible with the algorithm 
+     * family.
+     * 
+     * @param credential the credential to check
+     * @param algorithm the algorithm to check compatibility with
+     * @return true of the key is compatible with the algorithm, false otherwise
+     */
+    private boolean checkKeyWithAlgorithm(
+            @Nonnull final JWKCredential credential, @Nonnull final JWEAlgorithm algorithm) {
+        
+        if (credential.getAlgorithm() != null) {
+            // Have algorithm, so that must match exactly
+            return algorithm.equals(credential.getAlgorithm());
+        }
+        // Else check key type        
+        final Key key = CredentialSupport.extractEncryptionKey(credential);
+        
+        if (JWEAlgorithm.Family.RSA.contains(algorithm) && key.getAlgorithm().equals("RSA")) {
+            return true;
+        } else if (JWEAlgorithm.Family.ECDH_ES.contains(algorithm) && key.getAlgorithm().equals("EC")) {
+            return true;
+        } else if (JWEAlgorithm.Family.SYMMETRIC.contains(algorithm) && key.getAlgorithm().equals("AES")) {
+            return true;
+        }  
+        return false;
+
+    }
+    
     /**
      * Find an encryption method that is supported by the credential. That is, supports the algorithm and
      * has the correct key length.
@@ -643,6 +675,7 @@ public class BasicJWTEncryptionParametersResolver extends AbstractSecurityParame
         return AlgorithmSupport.checkKeyAlgorithmAndLength(key, getAlgorithmRegistry().get(algorithm));
     }
     
+    
     /**
      * Evaluate whether the specified algorithm is a key encryption or key wrapping algorithm.
      * 
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java
index 6b2184e..d4df4ee 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolver.java
@@ -17,6 +17,7 @@
 
 package net.shibboleth.oidc.security.impl;
 
+import java.security.Key;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Collections;
@@ -29,6 +30,8 @@ import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,7 +42,6 @@ import com.nimbusds.jose.jwk.ECKey;
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.JWKSet;
 import com.nimbusds.jose.jwk.KeyType;
-import com.nimbusds.jose.jwk.KeyUse;
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 
@@ -50,6 +52,7 @@ import net.shibboleth.oidc.security.credential.BasicJWKCredential;
 import net.shibboleth.oidc.security.criterion.JWKSetCriterion;
 import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
 import net.shibboleth.oidc.security.criterion.ProviderMetadataCriterion;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
 import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.logic.FunctionSupport;
@@ -270,8 +273,10 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicJWTEncryp
         final JWK key = 
                 providerKeySet.getKeys().stream()
                 .filter(Objects::nonNull)
-                .filter(k -> KeyUse.ENCRYPTION == k.getKeyUse())
-                .filter(k -> k.getAlgorithm().equals(algorithm))
+                .filter(k ->  UsageType.ENCRYPTION == CredentialConversionUtil.getUsageType(k) 
+                           || UsageType.UNSPECIFIED == CredentialConversionUtil.getUsageType(k))
+                .filter(k -> checkKeyAlgorithmAndLength(k, algorithm.getName()))
+                .filter(k -> checkKeyTypeWithAlgorithm(k, algorithm))
                 .findFirst().orElse(null);
         
         if (key != null) {
@@ -288,13 +293,86 @@ public class ProviderMetadataEncryptionParametersResolver extends BasicJWTEncryp
                 log.warn("Unable to parse keyset", e);
                 return;
             }
-            if (checkKeyAlgorithmAndLength(jwkCredential, algorithm.getName())) {
-                log.debug("Selected key '{}' for alg {} and enc {}", key.getKeyID(), 
-                        algorithm.getName(), encryptionMethod.getName());
-                params.setKeyTransportEncryptionCredential(jwkCredential);
-                params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
-                params.setDataEncryptionAlgorithm(encryptionMethod.getName()); 
+            log.debug("Selected key '{}' for alg {} and enc {}", key.getKeyID(), 
+                    algorithm.getName(), encryptionMethod.getName());
+            params.setKeyTransportEncryptionCredential(jwkCredential);
+            params.setKeyTransportEncryptionAlgorithm(algorithm.getName());
+            params.setDataEncryptionAlgorithm(encryptionMethod.getName()); 
+            
+        }
+        
+    }
+    
+    /**
+     * Check the JWK supports the algorithm specified. If 'alg' is present, check that is identical to the 
+     * supplied algorithm. If not, check the 'kty' or key type parameter of the JWK is compatible with the 
+     * algorithm family.
+     * 
+     * @param jwk the JSON web key
+     * @param algorithm the algorithm to check compatibility with
+     * 
+     * @return true of the key is compatible with the algorithm, false otherwise
+     */
+    private boolean checkKeyTypeWithAlgorithm(@Nonnull final JWK jwk, @Nonnull final JWEAlgorithm algorithm) {
+        if (jwk.getAlgorithm() != null) {
+            // Have algorithm, so that must match exactly
+            return algorithm.equals(jwk.getAlgorithm());
+        }
+        if (JWEAlgorithm.Family.RSA.contains(algorithm) && jwk.getKeyType().equals(KeyType.RSA)) {
+            return true;
+        } else if (JWEAlgorithm.Family.ECDH_ES.contains(algorithm) && jwk.getKeyType().equals(KeyType.EC)) {
+            return true;
+        } else if (JWEAlgorithm.Family.SYMMETRIC.contains(algorithm) && jwk.getKeyType().equals(KeyType.OCT)) {
+            return true;
+        }  
+        return false;
+    }
+    
+    /**
+     * Evaluate whether the specified JWK key is supported for use with the specified algorithm URI
+     * and the key length matches.
+     * 
+     * @param jwkKey the JWK to evaluate
+     * @param algorithm the algorithm URI to evaluate against
+     * 
+     * @return true if credential may be used with the supplied algorithm URI and the key length matches, 
+     *          false otherwise
+     */
+    private boolean checkKeyAlgorithmAndLength(@Nonnull final JWK jwk, 
+            @Nonnull @NotEmpty final String algorithm) {
+        
+        final Key key = extractEncryptionKeyFromJWK(jwk);
+        if (key == null) {
+            return false;
+        }
+        
+        return AlgorithmSupport.checkKeyAlgorithmAndLength(key, getAlgorithmRegistry().get(algorithm));
+    }
+    
+    /**
+     * Extract the encryption key from the JWK.
+     * 
+     * @param jwk the JWK containing the encryption key
+     * @return the encryption key (either a public key or a secret (symmetric) key
+     */
+    @Nullable private Key extractEncryptionKeyFromJWK(@Nullable final JWK jwk) {
+        if (jwk == null) {
+            return null;
+        }
+        try {
+            if (jwk.getKeyType() == KeyType.EC) {
+                return jwk.toECKey().toPublicKey();
+            } else if (jwk.getKeyType() == KeyType.RSA) {
+                return jwk.toRSAKey().toPublicKey();
+            } else if (jwk.getKeyType() == KeyType.OCT) {
+                return jwk.toOctetSequenceKey().toSecretKey();
+            } else {
+                return null;
             }
+            //TODO support for OKP
+        } catch (final JOSEException e) {
+            log.trace("Unable to extract encryption key from JWK '{}'", jwk.getKeyID());
+            return null;
         }
         
     }
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
index ceb9014..25c83b1 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/BasicJWTEncryptionParametersResolverTest.java
@@ -24,19 +24,13 @@ import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
-import java.security.KeyException;
-import java.time.Duration;
 import java.util.List;
 
 import org.opensaml.core.config.InitializationException;
-import org.opensaml.security.credential.UsageType;
-import org.opensaml.security.crypto.KeySupport;
 import org.opensaml.xmlsec.config.GlobalAlgorithmRegistryInitializer;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.nimbusds.jose.Algorithm;
-import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWEAlgorithm;
 import com.nimbusds.jose.jwk.Curve;
 import com.nimbusds.jose.jwk.ECKey;
@@ -48,7 +42,7 @@ import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
 import net.shibboleth.oidc.jwa.support.EncryptionConstants;
 import net.shibboleth.oidc.jwa.support.KeyManagementConstants;
 import net.shibboleth.oidc.security.JWTEncryptionParameters;
-import net.shibboleth.oidc.security.credential.BasicExpiringJWKCredential;
+import net.shibboleth.oidc.security.credential.BasicJWKCredential;
 import net.shibboleth.oidc.security.credential.JWKCredential;
 import net.shibboleth.oidc.security.criterion.JWTEncryptionConfigurationCriterion;
 import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -107,6 +101,32 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
+       
+    }
+    
+    @Test
+    public void testBasicRSA_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        final RSAKey key = new RSAKeyGenerator(2048)
+                .algorithm(JWEAlgorithm.RSA_OAEP_256)
+                .keyUse(KeyUse.ENCRYPTION)
+                .keyID("mock-key")
+                .generate();
+        final JWKCredential cred = TestCredentialHelper.createKeyEncryptionCredential(key);
+        // Blank algorithm, this should work on the 'key algorithm' alone 
+        ((BasicJWKCredential)cred).setAlgorithm(null);
+        config.setKeyTransportEncryptionCredentials(List.of(cred));
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+        
+        assertNotNull(param);
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
        
     }
     
@@ -130,6 +150,36 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_ECDH_ES);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
+       
+    }
+    
+    @Test
+    public void testBasicEC_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
+        final ECKey key = new ECKeyGenerator(Curve.P_256)                
+                .keyUse(KeyUse.ENCRYPTION)
+                .algorithm(JWEAlgorithm.ECDH_ES)
+                .keyID("mock-key")
+                .generate();
+        
+        final JWKCredential cred = TestCredentialHelper.createKeyAgreementCredential(key);
+        ((BasicJWKCredential)cred).setAlgorithm(null);
+        config.setKeyTransportEncryptionCredentials(List.of(cred));
+        
+        config.setKeyTransportEncryptionCredentials(List.of(cred));
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+        
+        assertNotNull(param);
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_ECDH_ES);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
        
     }
     
@@ -149,6 +199,32 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
+       
+    }
+
+    @Test
+    public void testBasicAESKeyWrap_NoAlgorithmSpecifiedInJWKCredential() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(
+                List.of(KeyManagementConstants.ALGO_ID_ALG_AES_256_KW));  
+        
+        final JWKCredential cred = TestCredentialHelper.createSharedSecretCredential("mock-key",
+                SYMMETRIC_KEY, JWEAlgorithm.A256KW);
+        // Blank algorithm, this should work on the 'key algorithm' alone 
+        ((BasicJWKCredential)cred).setAlgorithm(null);
+        
+        config.setKeyTransportEncryptionCredentials(List.of(cred));
+        final JWTEncryptionParameters param = resolver.resolveSingle(criteria);
+        
+        assertNotNull(param);
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A128CBC_HS256);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_AES_256_KW);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getSecretKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
        
     }
     
@@ -169,6 +245,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
         assertNotNull(param.getDataEncryptionCredential());
         assertNotNull(param.getDataEncryptionCredential().getSecretKey());
+        assertEquals(param.getDataEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
 
     }
     
@@ -189,6 +266,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_DIR);
         assertNotNull(param.getDataEncryptionCredential());
         assertNotNull(param.getDataEncryptionCredential().getSecretKey());
+        assertEquals(param.getDataEncryptionCredential().getSecretKey().getAlgorithm(),"AES");
     }
     
     /* The key is 256bit and does not support the 128bit enc. algo.*/
@@ -254,6 +332,7 @@ public class BasicJWTEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP_256);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
     }
     
     @Test
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java
index 4d74ec7..7678d84 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/impl/ProviderMetadataEncryptionParametersResolverTest.java
@@ -83,6 +83,14 @@ public class ProviderMetadataEncryptionParametersResolverTest {
     private static final ClassPathResource REMOTE_JWKSET_NO_ALG_PARAM = 
             new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk"); 
     
+    /** A remote JWKSet with no 'alg' parameters.*/
+    private static final ClassPathResource REMOTE_JWKSET_NO_KEY_USE = 
+            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk"); 
+    
+    /** A remote JWKSet where key 87ff206d-15f9-4b8c-ba88-a8c17014da13 has the wrong key use.*/
+    private static final ClassPathResource REMOTE_JWKSET_WRONG_KEYUSE = 
+            new ClassPathResource("/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk");
+    
     /** The client_secret.*/
     private static final String CLIENT_SECRET = "Xp2s5v8y/B?E(H+MbQeThWmYq3t6w9z$";
     
@@ -98,6 +106,9 @@ public class ProviderMetadataEncryptionParametersResolverTest {
     /** The basic config.*/
     private BasicJWTEncryptionConfiguration config;
     
+    /** The cache being used by the resolver.*/
+    private RemoteJwkSetCache cache;
+    
     /**
      * Read a file into a string.
      * 
@@ -128,7 +139,7 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         resolver = new ProviderMetadataEncryptionParametersResolver();
         resolver.setProviderEncryptionMethodsLookupStrategy(OIDCProviderMetadata::getRequestObjectJWEEncs);
         resolver.setProviderKeyTransportAlgorithmsLookupStrategy(OIDCProviderMetadata::getRequestObjectJWEAlgs);
-        final RemoteJwkSetCache cache = new RemoteJwkSetCache();
+        cache = new RemoteJwkSetCache();
         cache.setStorage(buildStorageService());
         cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET)));
         resolver.setRemoteJwkSetCache(cache);
@@ -172,16 +183,44 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         final Iterable<JWTEncryptionParameters> params = resolver.resolve(buildBasicCriteriaSet());
         assertNotNull(params);
         assertTrue(params.iterator().hasNext());
+    }
+    
+    /* Algorithms are know because they are limited by config.*/
+    @Test
+    public void testSuccessfulResolution_ForKeyEncryption() throws Exception {
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
         final JWTEncryptionParameters param = params.iterator().next();
         assertNotNull(param.getDataEncryptionAlgorithm());
-        assertNotNull(param.getKeyTransportEncryptionAlgorithm()); 
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
     }
     
-    /* Algorithms are know because they are limited by config.*/
+    /* Key 87ff206d-15f9-4b8c-ba88-a8c17014da13 has a key use of 'sig' rather than 'end'.*/
     @Test
-    public void testSuccessfulResolution_ForKeyEncryption() throws Exception {
+    public void testUnSuccessfulResolution_ForKeyEncryption_WrongKeyUse() throws Exception {
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_WRONG_KEYUSE)));
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertFalse(params.iterator().hasNext());        
+    }
+    
+    /* Key 87ff206d-15f9-4b8c-ba88-a8c17014da13 has no key use, so assume unspecified.*/
+    @Test
+    public void testSuccessfulResolution_ForKeyEncryption_NoKeyUse() throws Exception {
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_KEY_USE)));
         final CriteriaSet criteria =  buildBasicCriteriaSet();
         config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
         config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
@@ -196,6 +235,49 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
+    }
+    
+    /* Test credential choice if the keys do not specify their 'alg', only the mandatory 'kty'*/
+    @Test
+    public void testSuccessfulResolution_ForKeyEncryption_NoAlgParams() throws Exception {
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_ALG_PARAM)));
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
+    }
+    
+    /* Test credential choice if the keys do not specify their 'alg', only the mandatory 'kty'*/
+    @Test
+    public void testSuccessfulResolution_ForKeyAgreement_NoAlgParams() throws Exception {
+        cache.setHttpClient(createMockHttpClient(readJsonFromFile(REMOTE_JWKSET_NO_ALG_PARAM)));
+        final CriteriaSet criteria =  buildBasicCriteriaSet();
+        config.setKeyTransportEncryptionAlgorithms(List.of(KeyManagementConstants.ALGO_ID_ALG_ECDH_ES));
+        config.setDataEncryptionAlgorithms(List.of(EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512));
+        
+        final Iterable<JWTEncryptionParameters> params = resolver.resolve(criteria);
+        assertNotNull(params);
+        assertTrue(params.iterator().hasNext());
+        final JWTEncryptionParameters param = params.iterator().next();
+        assertNotNull(param.getDataEncryptionAlgorithm());
+        assertNotNull(param.getKeyTransportEncryptionAlgorithm());  
+        assertEquals(param.getDataEncryptionAlgorithm(),EncryptionConstants.ALGO_ID_ENC_ALG_A256CBC_HS512);
+        assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_ECDH_ES);
+        assertNotNull(param.getKeyTransportEncryptionCredential());
+        assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
     }
     
     /* Should chose key encryption creds as they are the only ones configured, and are first
@@ -217,6 +299,7 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_RSA_OAEP);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
     }
     
     /* Should chose key wrap creds first, as that algorithm is first in the list.*/
@@ -320,6 +403,7 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertTrue(param.getKeyTransportEncryptionCredential().getKeyNames().contains("mock-key"));
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"RSA");
     }
     
     /* The first mockKey in the list should be resolved.*/
@@ -376,6 +460,7 @@ public class ProviderMetadataEncryptionParametersResolverTest {
         assertEquals(param.getKeyTransportEncryptionAlgorithm(),KeyManagementConstants.ALGO_ID_ALG_ECDH_ES);
         assertNotNull(param.getKeyTransportEncryptionCredential());
         assertNotNull(param.getKeyTransportEncryptionCredential().getPublicKey());
+        assertEquals(param.getKeyTransportEncryptionCredential().getPublicKey().getAlgorithm(),"EC");
     }
     
     @Test
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
index 5473bee..30639e2 100644
--- a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
@@ -50,7 +50,6 @@
 "e": "AQAB",
 "use": "enc",
 "kid": "87ff206d-15f9-4b8c-ba88-a8c17014da13",
-"alg": "RSA-OAEP",
 "n": "uAVnVD3cMEbrAsDg1c3n6GfzR3sSg9C9pbjTw39_jgWk5YQCHPPOt4zYyZZL2JCnm9TFjnndCCW5ZPWHPJjumiNB2r-vC0CmI-T66JSRX3YYw0h2Odiusr_74FNe_mYyEuClFa4hwo-RMgrp8L1sbrAWcgGOc84rD6-fZXVrWFMkOb0jg6tqF1EwBSxZFG1cfvUmatNuBXs6njPHvvqhd7Bz6adK4YkpzCUbD-jSjpvAvU-Q4TZT_bXq4WRFOPqXv2NX4ch7ErjEm5tJEk7BIqOh7Byg0pWB4WAwsMcZKnHlp7JjtB2T1s_45_iqD2xipxpF-NxoHUlz67qHt7-W4Q"
 },
 {
@@ -59,8 +58,7 @@
 "crv": "P-256",
 "kid": "c689ce91-8d82-45f2-b671-38ee38e7599f",
 "x": "redOUw802EuKJRoS8kQx6_RjuCypx0dcMBhv4IAALvQ",
-"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk",
-"alg": "ECDH-ES"
+"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk"
 }
 ]
 }
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk
similarity index 95%
copy from oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
copy to oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk
index 5473bee..10e7a50 100644
--- a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-keyuse.jwk
@@ -48,9 +48,7 @@
 {
 "kty": "RSA",
 "e": "AQAB",
-"use": "enc",
 "kid": "87ff206d-15f9-4b8c-ba88-a8c17014da13",
-"alg": "RSA-OAEP",
 "n": "uAVnVD3cMEbrAsDg1c3n6GfzR3sSg9C9pbjTw39_jgWk5YQCHPPOt4zYyZZL2JCnm9TFjnndCCW5ZPWHPJjumiNB2r-vC0CmI-T66JSRX3YYw0h2Odiusr_74FNe_mYyEuClFa4hwo-RMgrp8L1sbrAWcgGOc84rD6-fZXVrWFMkOb0jg6tqF1EwBSxZFG1cfvUmatNuBXs6njPHvvqhd7Bz6adK4YkpzCUbD-jSjpvAvU-Q4TZT_bXq4WRFOPqXv2NX4ch7ErjEm5tJEk7BIqOh7Byg0pWB4WAwsMcZKnHlp7JjtB2T1s_45_iqD2xipxpF-NxoHUlz67qHt7-W4Q"
 },
 {
@@ -59,8 +57,7 @@
 "crv": "P-256",
 "kid": "c689ce91-8d82-45f2-b671-38ee38e7599f",
 "x": "redOUw802EuKJRoS8kQx6_RjuCypx0dcMBhv4IAALvQ",
-"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk",
-"alg": "ECDH-ES"
+"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk"
 }
 ]
 }
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk
similarity index 95%
copy from oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
copy to oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk
index 5473bee..a388099 100644
--- a/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-no-alg.jwk
+++ b/oidc-common-crypto-impl/src/test/resources/credentials/test-provider-resolver-remote-jwkset-response-wrong-key-use.jwk
@@ -48,9 +48,8 @@
 {
 "kty": "RSA",
 "e": "AQAB",
-"use": "enc",
+"use": "sig",
 "kid": "87ff206d-15f9-4b8c-ba88-a8c17014da13",
-"alg": "RSA-OAEP",
 "n": "uAVnVD3cMEbrAsDg1c3n6GfzR3sSg9C9pbjTw39_jgWk5YQCHPPOt4zYyZZL2JCnm9TFjnndCCW5ZPWHPJjumiNB2r-vC0CmI-T66JSRX3YYw0h2Odiusr_74FNe_mYyEuClFa4hwo-RMgrp8L1sbrAWcgGOc84rD6-fZXVrWFMkOb0jg6tqF1EwBSxZFG1cfvUmatNuBXs6njPHvvqhd7Bz6adK4YkpzCUbD-jSjpvAvU-Q4TZT_bXq4WRFOPqXv2NX4ch7ErjEm5tJEk7BIqOh7Byg0pWB4WAwsMcZKnHlp7JjtB2T1s_45_iqD2xipxpF-NxoHUlz67qHt7-W4Q"
 },
 {
@@ -59,8 +58,7 @@
 "crv": "P-256",
 "kid": "c689ce91-8d82-45f2-b671-38ee38e7599f",
 "x": "redOUw802EuKJRoS8kQx6_RjuCypx0dcMBhv4IAALvQ",
-"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk",
-"alg": "ECDH-ES"
+"y": "oaypduaS_wPLGCDQfJ0SKtQu0urJzhr4ZRn5wYMhiyk"
 }
 ]
 }
\ No newline at end of file

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


More information about the commits mailing list