From dd02d0c13995e30cfd6692ca86c02c5dbb3e23c0 Mon Sep 17 00:00:00 2001 From: Maciej Mierzwa Date: Mon, 23 Oct 2023 19:26:53 +0200 Subject: [PATCH] test cxf, nimbus compability Signed-off-by: Maciej Mierzwa --- build.gradle | 4 + .../security/authtoken/jwt/JwtVendor.java | 4 +- .../jwt/CxfNimbusCompabilityTest.java | 129 ++++++++++++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/opensearch/security/authtoken/jwt/CxfNimbusCompabilityTest.java diff --git a/build.gradle b/build.gradle index 6c75a0b2e7..581907c50a 100644 --- a/build.gradle +++ b/build.gradle @@ -509,6 +509,10 @@ dependencies { implementation "io.jsonwebtoken:jjwt-api:${jjwt_version}" implementation "io.jsonwebtoken:jjwt-impl:${jjwt_version}" implementation "io.jsonwebtoken:jjwt-jackson:${jjwt_version}" + implementation("org.apache.cxf:cxf-rt-rs-security-jose:${apache_cxf_version}") { + exclude(group: 'jakarta.activation', module: 'jakarta.activation-api') + } + // JSON flattener implementation ("com.github.wnameless.json:json-base:2.4.3") { exclude group: "org.glassfish", module: "jakarta.json" diff --git a/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java b/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java index 30e48dca97..0461df2e55 100644 --- a/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java +++ b/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java @@ -99,8 +99,8 @@ static Tuple createJwkFromSettings(Settings settings) { String paddedSecret = padSecret(new String(decoded), JWSAlgorithm.HS512); key = new OctetSequenceKey.Builder(paddedSecret.getBytes(StandardCharsets.UTF_8)).algorithm(JWSAlgorithm.HS512) - .keyUse(KeyUse.SIGNATURE) - .build(); + .keyUse(KeyUse.SIGNATURE) + .build(); } try { diff --git a/src/test/java/org/opensearch/security/authtoken/jwt/CxfNimbusCompabilityTest.java b/src/test/java/org/opensearch/security/authtoken/jwt/CxfNimbusCompabilityTest.java new file mode 100644 index 0000000000..de621b5759 --- /dev/null +++ b/src/test/java/org/opensearch/security/authtoken/jwt/CxfNimbusCompabilityTest.java @@ -0,0 +1,129 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.security.authtoken.jwt; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.MACVerifier; +import com.nimbusds.jose.crypto.factories.DefaultJWSSignerFactory; +import com.nimbusds.jose.jwk.OctetSequenceKey; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import org.apache.commons.lang3.StringUtils; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureProvider; +import org.apache.cxf.rs.security.jose.jws.HmacJwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.Base64; +import java.util.Date; + +import org.apache.cxf.rs.security.jose.jwt.JoseJwtProducer; + +public class CxfNimbusCompabilityTest { + + @Test + public void testGenerateCXFJwtValidateUsingNimbus() throws Exception { + // raw classes without projects JWTVendor wrapper class + // cxf + String shortSecret = "someShortSecret1"; + cxfToNimbusJwtVerification(shortSecret); + // perfectLength + String sixFourBytesSecret = "someSixFourBytesSecretsomeSixFourBytesSecretsomeSixFourBytesSecr"; + cxfToNimbusJwtVerification(sixFourBytesSecret); + // longer + String longSecret = + "someReallyLongSecretsomeReallyLongSecretsomeReallyLongSecretsomeReallyLongSecretsomeReallyLongSecretsomeReallyLongSecret"; + cxfToNimbusJwtVerification(longSecret); + } + + private static void cxfToNimbusJwtVerification(String someSecret) throws ParseException, JOSEException { + String base64EncodedSecret = Base64.getEncoder().encodeToString(someSecret.getBytes(StandardCharsets.UTF_8)); + + // claims + JwtClaims claims = new JwtClaims(); + claims.setIssuedAt(100L); + claims.setIssuer("cluster_0"); + // token + JwtToken jwtToken = new JwtToken(claims); + + JwsSignatureProvider jwsSignatureProvider = new HmacJwsSignatureProvider(base64EncodedSecret, SignatureAlgorithm.HS512); + JweEncryptionProvider jweEncryptionProvider = null; + + JoseJwtProducer producer = new JoseJwtProducer(); + String encodedCxfJwt = producer.processJwt(jwtToken, jweEncryptionProvider, jwsSignatureProvider); + + // parse using nimbus lib + SignedJWT signedJWT = SignedJWT.parse(encodedCxfJwt); + // nimbus verifier + JWSVerifier verifier = new MACVerifier(StringUtils.rightPad(someSecret, 64, "\0")); + + Assert.assertTrue(signedJWT.verify(verifier)); + } + + @Test + public void testGenerateNimbusJwtValidateUsingCxf() throws Exception { + // raw classes without projects JWTVendor wrapper class + // nimbus + String shortSecret = "someShortSecret"; + nimbusToCxfJwtVerification(shortSecret); + // perfectLength + String sixFourBytesSecret = "someSixFourBytesSecretsomeSixFourBytesSecretsomeSixFourBytesSecr"; + nimbusToCxfJwtVerification(sixFourBytesSecret); + // longer + String longSecret = + "someReallyLongSecretsomeReallyLongSecretsomeReallyLongSecretsomeReallyLongSecretsomeReallyLongSecretsomeReallyLongSecret"; + nimbusToCxfJwtVerification(longSecret); + } + + private static void nimbusToCxfJwtVerification(String secret) throws JOSEException { + String base64EncodedSecret = Base64.getEncoder() + .encodeToString(StringUtils.rightPad(secret, 64, "\0").getBytes(StandardCharsets.UTF_8)); + + // claims + JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS512).build(); + JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer("cluster_0").issueTime(new Date(100L)).build(); + + SignedJWT signedJWT = new SignedJWT(header, claimsSet); + // nimbus signer + Base64URL base64URL = new Base64URL(base64EncodedSecret); + OctetSequenceKey key = new OctetSequenceKey.Builder(Base64.getDecoder().decode(base64EncodedSecret)).build(); + signedJWT.sign(new DefaultJWSSignerFactory().createJWSSigner(key)); + + String encodedNimbusJwt = signedJWT.serialize(); + + // parse using cxf lib and validate signature + JweDecryptionProvider theDecryptor = null; + // no padding at this point + JwsSignatureVerifier theSigVerifier = new HmacJwsSignatureVerifier( + Base64.getEncoder().encodeToString(secret.getBytes(StandardCharsets.UTF_8)), + SignatureAlgorithm.HS512 + ); + // padding added, shouldn't matter + JwsSignatureVerifier theSigVerifierPadded = new HmacJwsSignatureVerifier(base64EncodedSecret, SignatureAlgorithm.HS512); + + // signature validation inside + new JoseJwtConsumer().getJwtToken(encodedNimbusJwt, theDecryptor, theSigVerifier); + new JoseJwtConsumer().getJwtToken(encodedNimbusJwt, theDecryptor, theSigVerifierPadded); + } + +}