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


The following commit(s) were added to refs/heads/main by this push:
     new 35d43a6916 iTests for supported JWE algorithms
35d43a6916 is described below

commit 35d43a6916c5318a68f3401c31bf00db38a32c15
Author: David Blevins <dblev...@tomitribe.com>
AuthorDate: Thu Oct 6 17:04:27 2022 -0500

    iTests for supported JWE algorithms
---
 itests/microprofile-jwt-itests/pom.xml             |  4 +-
 ...izesTest.java => EncryptionAlgorithmsTest.java} | 77 ++++++++++++++------
 .../microprofile/jwt/itest/RsaKeySizesTest.java    |  5 --
 .../tomee/microprofile/jwt/itest/Tokens.java       | 81 +++++++++++++++++++---
 .../apache/tomee/microprofile/jwt/MPJWTFilter.java |  1 +
 5 files changed, 131 insertions(+), 37 deletions(-)

diff --git a/itests/microprofile-jwt-itests/pom.xml 
b/itests/microprofile-jwt-itests/pom.xml
index 080e4e8acb..2a20483ad5 100644
--- a/itests/microprofile-jwt-itests/pom.xml
+++ b/itests/microprofile-jwt-itests/pom.xml
@@ -89,7 +89,7 @@
     <dependency>
       <groupId>com.nimbusds</groupId>
       <artifactId>nimbus-jose-jwt</artifactId>
-      <version>4.23</version>
+      <version>9.25.4</version>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -101,7 +101,7 @@
     <dependency>
       <groupId>io.churchkey</groupId>
       <artifactId>churchkey</artifactId>
-      <version>1.21</version>
+      <version>1.22</version>
       <scope>test</scope>
     </dependency>
     <dependency>
diff --git 
a/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
 
b/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/EncryptionAlgorithmsTest.java
similarity index 64%
copy from 
itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
copy to 
itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/EncryptionAlgorithmsTest.java
index de7a87aafa..a8ed538a2e 100644
--- 
a/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
+++ 
b/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/EncryptionAlgorithmsTest.java
@@ -18,16 +18,11 @@
 
 package org.apache.tomee.microprofile.jwt.itest;
 
-import org.apache.cxf.feature.LoggingFeature;
-import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.johnzon.jaxrs.JohnzonProvider;
-import org.apache.tomee.server.composer.Archive;
-import org.apache.tomee.server.composer.TomEE;
-import org.eclipse.microprofile.auth.LoginConfig;
-import org.junit.Test;
-
 import jakarta.annotation.security.RolesAllowed;
 import jakarta.enterprise.context.RequestScoped;
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
 import jakarta.ws.rs.ApplicationPath;
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.GET;
@@ -36,6 +31,15 @@ import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
+import org.apache.cxf.feature.LoggingFeature;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.johnzon.jaxrs.JohnzonProvider;
+import org.apache.tomee.server.composer.Archive;
+import org.apache.tomee.server.composer.TomEE;
+import org.eclipse.microprofile.auth.LoginConfig;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.net.URL;
 import java.util.Base64;
@@ -43,36 +47,55 @@ import java.util.Base64;
 import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
 
-public class RsaKeySizesTest {
+public class EncryptionAlgorithmsTest {
 
     @Test
-    public void test1024() throws Exception {
-        assertKey(Tokens.rsa(1024, 256));
+    public void rsaOaep() throws Exception {
+        assertAlgorithm(Tokens.rsa(2048, "RSA-OAEP"), Tokens.rsa(2048, 256));
     }
 
     @Test
-    public void test2048() throws Exception {
-        assertKey(Tokens.rsa(2048, 256));
+    public void rsaOaep256() throws Exception {
+        assertAlgorithm(Tokens.rsa(2048, "RSA-OAEP-256"), Tokens.rsa(2048, 
256));
     }
 
     @Test
-    public void test4096() throws Exception {
-        assertKey(Tokens.rsa(4096, 256));
+    public void ecdhEs() throws Exception {
+        assertAlgorithm(Tokens.ec("secp256r1", "ECDH-ES"), 
Tokens.ec("secp256r1", 256));
     }
 
-    public void assertKey(final Tokens tokens) throws Exception {
+    @Test
+    public void ecdhEsA128KW() throws Exception {
+        assertAlgorithm(Tokens.ec("secp256r1", "ECDH-ES+A128KW"), 
Tokens.ec("secp256r1", 256));
+    }
+
+    @Test
+    public void ecdhEsA192KW() throws Exception {
+        assertAlgorithm(Tokens.ec("secp256r1", "ECDH-ES+A192KW"), 
Tokens.ec("secp256r1", 256));
+    }
+
+    @Test
+    public void ecdhEsA256KW() throws Exception {
+        assertAlgorithm(Tokens.ec("secp256r1", "ECDH-ES+A256KW"), 
Tokens.ec("secp256r1", 256));
+    }
+
+    public void assertAlgorithm(final Tokens encryptor, final Tokens singer) 
throws Exception {
         final File appJar = Archive.archive()
-                .add(RsaKeySizesTest.class)
+                .add(EncryptionAlgorithmsTest.class)
                 .add(ColorService.class)
                 .add(Api.class)
+                .add("signer.pem", singer.getPemPublicKey())
+                .add("encryptor.pem", encryptor.getPemPrivateKey())
                 .add("META-INF/microprofile-config.properties", "#\n" +
-                        "mp.jwt.verify.publickey=" + 
Base64.getEncoder().encodeToString(tokens.getPublicKey().getEncoded()))
+                        "mp.jwt.decrypt.key.location=encryptor.pem\n" +
+                        "mp.jwt.verify.publickey.location=signer.pem\n")
                 .asJar();
 
         final TomEE tomee = TomEE.microprofile()
                 .add("webapps/test/WEB-INF/beans.xml", "")
                 .add("webapps/test/WEB-INF/lib/app.jar", appJar)
 //                .update()
+//                .debug(5005)
                 .build();
 
         final WebClient webClient = 
createWebClient(tomee.toURI().resolve("/test").toURL());
@@ -86,7 +109,10 @@ public class RsaKeySizesTest {
                 "}";
 
         {// valid token
-            final String token = tokens.asToken(claims);
+            final String token = encryptor.asEncryptedToken(claims, singer);
+
+            assertAlg(encryptor.getAlg(), token);
+
             final Response response = webClient.reset()
                     .path("/movies")
                     .header("Content-Type", "application/json")
@@ -96,7 +122,7 @@ public class RsaKeySizesTest {
         }
 
         {// invalid token
-            final String token = tokens.asToken(claims) + "a";
+            final String token = "a" + encryptor.asEncryptedToken(claims, 
singer);
             final Response response = webClient.reset()
                     .path("/movies")
                     .header("Content-Type", "application/json")
@@ -106,6 +132,17 @@ public class RsaKeySizesTest {
         }
     }
 
+    private void assertAlg(final String expected, final String token) {
+        final String encodedHeader = token.split("\\.")[0];
+
+        final byte[] decoded = Base64.getDecoder().decode(encodedHeader);
+        final JsonReader reader = Json.createReader(new 
ByteArrayInputStream(decoded));
+        final JsonObject jsonObject = reader.readObject();
+        final String actual = jsonObject.getString("alg");
+
+        assertEquals(expected, actual);
+    }
+
     private static WebClient createWebClient(final URL base) {
         return WebClient.create(base.toExternalForm(), singletonList(new 
JohnzonProvider<>()),
                 singletonList(new LoggingFeature()), null);
diff --git 
a/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
 
b/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
index de7a87aafa..737cc46118 100644
--- 
a/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
+++ 
b/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/RsaKeySizesTest.java
@@ -45,11 +45,6 @@ import static org.junit.Assert.assertEquals;
 
 public class RsaKeySizesTest {
 
-    @Test
-    public void test1024() throws Exception {
-        assertKey(Tokens.rsa(1024, 256));
-    }
-
     @Test
     public void test2048() throws Exception {
         assertKey(Tokens.rsa(2048, 256));
diff --git 
a/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/Tokens.java
 
b/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/Tokens.java
index a9754a709a..5631f0b330 100644
--- 
a/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/Tokens.java
+++ 
b/itests/microprofile-jwt-itests/src/test/java/org/apache/tomee/microprofile/jwt/itest/Tokens.java
@@ -16,11 +16,18 @@
  */
 package org.apache.tomee.microprofile.jwt.itest;
 
+import com.nimbusds.jose.EncryptionMethod;
 import com.nimbusds.jose.JOSEObjectType;
+import com.nimbusds.jose.JWEAlgorithm;
+import com.nimbusds.jose.JWEHeader;
+import com.nimbusds.jose.JWEObject;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.Payload;
 import com.nimbusds.jose.Requirement;
+import com.nimbusds.jose.crypto.ECDHEncrypter;
 import com.nimbusds.jose.crypto.ECDSASigner;
+import com.nimbusds.jose.crypto.RSAEncrypter;
 import com.nimbusds.jose.crypto.RSASSASigner;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.SignedJWT;
@@ -36,6 +43,8 @@ import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
 import java.security.spec.ECGenParameterSpec;
 import java.util.Base64;
 
@@ -48,24 +57,36 @@ public class Tokens {
     private final PublicKey publicKey;
     private final int hashSize;
     private final String id;
-    private final String prefix;
+    private final String alg;
 
-    public Tokens(final PrivateKey privateKey, final PublicKey publicKey, 
final int hashSize, final String id, final String prefix) {
+    public Tokens(final PrivateKey privateKey, final PublicKey publicKey, 
final int hashSize, final String id, final String alg) {
         this.privateKey = privateKey;
         this.publicKey = publicKey;
         this.hashSize = hashSize;
         this.id = id;
-        this.prefix = prefix;
+        this.alg = alg;
 
     }
 
+    public String getAlg() {
+        return alg;
+    }
+
     public static Tokens ec(final String curveName, int hashSize) {
+        return ec(curveName, hashSize, "ES");
+    }
+
+    public static Tokens ec(final String curveName, final String alg) {
+        return ec(curveName, -1, alg);
+    }
+
+    public static Tokens ec(final String curveName, int hashSize, final String 
alg) {
         try {
             KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
             ECGenParameterSpec spec = new ECGenParameterSpec(curveName);
             keyGen.initialize(spec);
             final KeyPair pair = keyGen.generateKeyPair();
-            return new Tokens(pair.getPrivate(), pair.getPublic(), hashSize, 
null, "ES");
+            return new Tokens(pair.getPrivate(), pair.getPublic(), hashSize, 
null, alg);
         } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException 
e) {
             throw new IllegalStateException(e);
         }
@@ -84,11 +105,19 @@ public class Tokens {
     }
 
     public static Tokens rsa(int keyLength, int hashSize, final String id) {
+        return rsa(keyLength, hashSize, id, "RS");
+    }
+
+    public static Tokens rsa(int keyLength, final String alg) {
+        return rsa(keyLength, -1, null, alg);
+    }
+
+    public static Tokens rsa(int keyLength, int hashSize, final String id, 
final String rs) {
         try {
             KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
             keyGen.initialize(keyLength);
             final KeyPair pair = keyGen.generateKeyPair();
-            return new Tokens(pair.getPrivate(), pair.getPublic(), hashSize, 
id, "RS");
+            return new Tokens(pair.getPrivate(), pair.getPublic(), hashSize, 
id, rs);
         } catch (NoSuchAlgorithmException e) {
             throw new IllegalStateException(e);
         }
@@ -127,8 +156,12 @@ public class Tokens {
     }
 
     public String asToken(final String claims) {
+        return sign(claims).serialize();
+    }
+
+    public SignedJWT sign(final String claims) {
         try {
-            final JWSHeader.Builder builder = new JWSHeader.Builder(new 
JWSAlgorithm(prefix + hashSize, Requirement.OPTIONAL))
+            final JWSHeader.Builder builder = new JWSHeader.Builder(new 
JWSAlgorithm(alg + hashSize, Requirement.OPTIONAL))
                     .type(JOSEObjectType.JWT);
 
             if (id != null) {
@@ -141,20 +174,48 @@ public class Tokens {
 
             final SignedJWT jwt = new SignedJWT(header, claimsSet);
 
-            if ("RS".equals(prefix)) {
+            if (alg.startsWith("RS")) {
                 jwt.sign(new RSASSASigner(privateKey));
-            } else if ("ES".equals(prefix)) {
+            } else if (alg.startsWith("ES")) {
                 jwt.sign(new ECDSASigner((ECPrivateKey) privateKey));
             } else {
-                throw new IllegalStateException("Unsupported prefix: " + 
prefix);
+                throw new IllegalStateException("Unsupported prefix: " + alg);
             }
 
-            return jwt.serialize();
+            return jwt;
         } catch (Exception e) {
             throw new RuntimeException("Could not sign JWT", e);
         }
     }
 
+    public String asEncryptedToken(final String claims, final Tokens signer) {
+        try {
+
+            final SignedJWT signedJWT = signer.sign(claims);
+
+            // Create JWE object with signed JWT as payload
+            final JWEObject jweObject = new JWEObject(
+                    new JWEHeader.Builder(new JWEAlgorithm(alg), 
EncryptionMethod.A256GCM)
+                            .contentType("JWT") // required to indicate nested 
JWT
+                            .build(),
+                    new Payload(signedJWT));
+
+            // Encrypt with the recipient's public key
+            if (alg.startsWith("RS")) {
+                jweObject.encrypt(new RSAEncrypter((RSAPublicKey) 
this.getPublicKey()));
+            } else if (alg.startsWith("EC")) {
+                jweObject.encrypt(new ECDHEncrypter((ECPublicKey) 
this.getPublicKey()));
+            } else {
+                throw new IllegalStateException("Unsupported prefix: " + alg);
+            }
+
+            // Serialise to JWE compact form
+            return jweObject.serialize();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not encrypt JWT", e);
+        }
+    }
+
     public static Tokens fromPrivateKey(final String contents) {
         final Key key;
         try {
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 948649718c..3bf36224d6 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
@@ -378,6 +378,7 @@ public class MPJWTFilter implements Filter {
             try {
                 final JwtConsumerBuilder builder = new JwtConsumerBuilder()
                         .setRelaxVerificationKeyValidation()
+                        .setRelaxDecryptionKeyValidation()
                         .setRequireSubject();
 
                 if (authContextInfo.getSignatureAlgorithm() != null) {

Reply via email to