This is an automated email from the ASF dual-hosted git repository.

dblevins pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit ab6e17dc8f0ea6d875c7107e1f39861b9dabf853
Author: David Blevins <dblev...@tomitribe.com>
AuthorDate: Fri Sep 9 09:20:39 2022 -0700

    TOMEE-3948 Decryption of JWTs using RSA-OAEP and A256GCM algorithms
---
 .../microprofile/jwt/JsonWebTokenValidator.java    |  2 +-
 .../apache/tomee/microprofile/jwt/MPJWTFilter.java | 36 ++++++++++++++++---
 .../jwt/config/JWTAuthConfiguration.java           | 40 ++++++++++++++--------
 .../jwt/config/JWTAuthConfigurationProperties.java | 10 ++++--
 4 files changed, 66 insertions(+), 22 deletions(-)

diff --git 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
index a0caf6d096..2178832bc2 100644
--- 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
+++ 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/JsonWebTokenValidator.java
@@ -85,7 +85,7 @@ public class JsonWebTokenValidator {
             if (authConfiguration.isSingleKey()) {
                 builder.setVerificationKey(authConfiguration.getPublicKey());
             } else {
-                builder.setVerificationKeyResolver(new 
JwksVerificationKeyResolver(authConfiguration.getPublicKeys()));
+                builder.setVerificationKeyResolver(new 
JwksVerificationKeyResolver(authConfiguration.getPublicKeysJwk()));
             }
 
             final JwtConsumer jwtConsumer = builder.build();
diff --git 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
index c9f7c5049c..adee42c135 100644
--- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
+++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
@@ -17,6 +17,7 @@
 package org.apache.tomee.microprofile.jwt;
 
 import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.spi.DeploymentException;
 import jakarta.inject.Inject;
 import jakarta.servlet.Filter;
 import jakarta.servlet.FilterChain;
@@ -42,6 +43,7 @@ import 
org.apache.tomee.microprofile.jwt.principal.JWTCallerPrincipal;
 import org.eclipse.microprofile.jwt.Claims;
 import org.eclipse.microprofile.jwt.JsonWebToken;
 import org.jose4j.jwa.AlgorithmConstraints;
+import org.jose4j.jwk.JsonWebKey;
 import org.jose4j.jws.AlgorithmIdentifiers;
 import org.jose4j.jwt.JwtClaims;
 import org.jose4j.jwt.MalformedClaimException;
@@ -50,14 +52,19 @@ import org.jose4j.jwt.consumer.InvalidJwtException;
 import org.jose4j.jwt.consumer.JwtConsumer;
 import org.jose4j.jwt.consumer.JwtConsumerBuilder;
 import org.jose4j.jwt.consumer.JwtContext;
+import org.jose4j.keys.resolvers.JwksDecryptionKeyResolver;
 import org.jose4j.keys.resolvers.JwksVerificationKeyResolver;
+import org.jose4j.lang.JoseException;
 
 import javax.security.auth.Subject;
 import java.io.IOException;
+import java.security.Key;
 import java.security.Principal;
 import java.util.Collections;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -277,7 +284,6 @@ public class MPJWTFilter implements Filter {
             this.jwtAuthConfiguration = authContextInfo;
         }
 
-
         public JsonWebToken validate(final HttpServletRequest request) {
 
             // not sure it's worth having synchronization inside a single 
request
@@ -357,12 +363,20 @@ public class MPJWTFilter implements Filter {
                     builder.setEvaluationTime(NumericDate.fromSeconds(0));
                 }
 
-                if (authContextInfo.isSingleKey()) {
+                if (authContextInfo.getPublicKeys().size() == 1) {
                     builder.setVerificationKey(authContextInfo.getPublicKey());
-                } else {
-                    builder.setVerificationKeyResolver(new 
JwksVerificationKeyResolver(authContextInfo.getPublicKeys()));
+                } else if (authContextInfo.getPublicKeys().size() > 1) {
+                    builder.setVerificationKeyResolver(new 
JwksVerificationKeyResolver(asJwks(authContextInfo.getPublicKeys())));
                 }
 
+                if (authContextInfo.getDecryptKeys().size() == 1){
+                    final Key decryptionKey = 
authContextInfo.getDecryptKeys().values().iterator().next();
+                    builder.setDecryptionKey(decryptionKey);
+                } else if (authContextInfo.getDecryptKeys().size() > 1) {
+                    builder.setDecryptionKeyResolver(new 
JwksDecryptionKeyResolver(asJwks(authContextInfo.getDecryptKeys())));
+                }
+                
+
                 final JwtConsumer jwtConsumer = builder.build();
                 final JwtContext jwtContext = jwtConsumer.process(token);
                 final String type = 
jwtContext.getJoseObjects().get(0).getHeader("typ");
@@ -392,5 +406,19 @@ public class MPJWTFilter implements Filter {
 
             return principal;
         }
+
+        public static List<JsonWebKey> asJwks(final Map<String, Key> keys) {
+            return keys.entrySet().stream().map(key -> {
+                try {
+                    final JsonWebKey jsonWebKey = 
JsonWebKey.Factory.newJwk(key.getValue());
+                    jsonWebKey.setKeyId(key.getKey());
+                    return jsonWebKey;
+                } catch (final JoseException e) {
+                    throw new DeploymentException(e);
+                }
+            }).collect(Collectors.toList());
+        }
     }
+
+
 }
diff --git 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java
 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java
index 930e9d7f23..cd66709355 100644
--- 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java
+++ 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java
@@ -16,15 +16,14 @@
  */
 package org.apache.tomee.microprofile.jwt.config;
 
+import org.apache.tomee.microprofile.jwt.MPJWTFilter;
 import org.jose4j.jwk.JsonWebKey;
-import org.jose4j.lang.JoseException;
 
 import java.security.Key;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;
-import java.util.stream.Collectors;
 
 /**
  * The public key and expected issuer needed to validate a token.
@@ -34,6 +33,7 @@ public class JWTAuthConfiguration {
     public static final String DEFAULT_KEY = "DEFAULT";
 
     private Map<String, Key> publicKeys;
+    private Map<String, Key> decryptKeys;
     private String[] audiences;
     private String issuer;
     private int expGracePeriodSecs = 60;
@@ -48,7 +48,7 @@ public class JWTAuthConfiguration {
         this.audiences = audiences;
     }
 
-    private JWTAuthConfiguration(final Map<String, Key> publicKeys, final 
String issuer, final boolean allowNoExpiryClaim, final String[] audiences) {
+    private JWTAuthConfiguration(final Map<String, Key> publicKeys, final 
String issuer, final boolean allowNoExpiryClaim, final String[] audiences, 
final Map<String, Key> decryptKeys) {
         if (publicKeys == null) {
             this.publicKeys = Collections.EMPTY_MAP;
         } else if (publicKeys.size() == 1) {
@@ -57,6 +57,13 @@ public class JWTAuthConfiguration {
         } else {
             this.publicKeys = Collections.unmodifiableMap(publicKeys);
         }
+
+        if (decryptKeys == null) {
+            this.decryptKeys = Collections.EMPTY_MAP;
+        } else {
+            this.decryptKeys = Collections.unmodifiableMap(decryptKeys);
+        }
+        
         this.issuer = issuer;
         this.allowNoExpiryClaim = allowNoExpiryClaim;
         this.audiences = audiences;
@@ -71,7 +78,11 @@ public class JWTAuthConfiguration {
     }
 
     public static JWTAuthConfiguration authConfiguration(final Map<String, 
Key> publicKeys, final String issuer, final boolean allowNoExpiryClaim, final 
String[] audiences) {
-        return new JWTAuthConfiguration(publicKeys, issuer, 
allowNoExpiryClaim, audiences);
+        return authConfiguration(publicKeys, issuer, allowNoExpiryClaim, 
audiences, null);
+    }
+
+    public static JWTAuthConfiguration authConfiguration(final Map<String, 
Key> publicKeys, final String issuer, final boolean allowNoExpiryClaim, final 
String[] audiences, final Map<String, Key> decryptKeys) {
+        return new JWTAuthConfiguration(publicKeys, issuer, 
allowNoExpiryClaim, audiences, decryptKeys);
     }
 
     public String[] getAudiences() {
@@ -86,17 +97,16 @@ public class JWTAuthConfiguration {
         return publicKeys.get(DEFAULT_KEY);
     }
 
-    public List<JsonWebKey> getPublicKeys() {
-        return publicKeys.entrySet().stream().map(key -> {
-            try {
-                final JsonWebKey jsonWebKey = 
JsonWebKey.Factory.newJwk(key.getValue());
-                jsonWebKey.setKeyId(key.getKey());
-                return jsonWebKey;
-            } catch (final JoseException e) {
-                logger.warning(e.getMessage());
-                return null;
-            }
-        }).collect(Collectors.toList());
+    public Map<String, Key> getPublicKeys() {
+        return publicKeys;
+    }
+
+    public Map<String, Key> getDecryptKeys() {
+        return decryptKeys;
+    }
+
+    public List<JsonWebKey> getPublicKeysJwk() {
+        return MPJWTFilter.ValidateJSonWebToken.asJwks(publicKeys);
     }
 
     public String getIssuer() {
diff --git 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
index 1697c92c7e..76ad9c9003 100644
--- 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
+++ 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java
@@ -32,6 +32,7 @@ import java.util.Map;
 import java.util.Optional;
 
 import static org.eclipse.microprofile.jwt.config.Names.AUDIENCES;
+import static org.eclipse.microprofile.jwt.config.Names.DECRYPTOR_KEY_LOCATION;
 import static org.eclipse.microprofile.jwt.config.Names.ISSUER;
 import static org.eclipse.microprofile.jwt.config.Names.VERIFIER_PUBLIC_KEY;
 import static 
org.eclipse.microprofile.jwt.config.Names.VERIFIER_PUBLIC_KEY_LOCATION;
@@ -92,10 +93,15 @@ public class JWTAuthConfigurationProperties {
         final Optional<String> publicKeyLocation = getPublicKeyLocation();
         final List<String> audiences = getAudiences();
 
-        final Map<String, Key> keys = new 
KeyResolver().resolvePublicKey(publicKeyContents, 
publicKeyLocation).orElse(null);
+        final Optional<String> decryptorKeyLocation = 
config.getOptionalValue(DECRYPTOR_KEY_LOCATION, String.class);
+
+        final KeyResolver resolver = new KeyResolver();
+        final Map<String, Key> publicKeys = 
resolver.resolvePublicKey(publicKeyContents, publicKeyLocation).orElse(null);
+        final Map<String, Key> decryptkeys = 
resolver.resolveDecryptKey(Optional.empty(), decryptorKeyLocation).orElse(null);
+
         final Boolean allowNoExp = 
config.getOptionalValue("mp.jwt.tomee.allow.no-exp", 
Boolean.class).orElse(false);
 
-        return JWTAuthConfiguration.authConfiguration(keys, 
getIssuer().orElse(null), allowNoExp, audiences.toArray(new String[0]));
+        return JWTAuthConfiguration.authConfiguration(publicKeys, 
getIssuer().orElse(null), allowNoExp, audiences.toArray(new String[0]), 
decryptkeys);
     }
 
 }

Reply via email to