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 <[email protected]>
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) {