diff --git a/build.gradle b/build.gradle index c5a8d52..585b2d3 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,11 @@ dependencies { compile 'org.apache.directory.studio:org.slf4j.log4j12:1.7.2' compile 'org.apache.logging.log4j:log4j:2.4.1' compile 'org.lable.rfc3881.auditlogger.adapter:slf4j:1.3' + + testCompile 'log4j:log4j:1.2.17' + testCompile 'org.apache.directory.studio:org.slf4j.log4j12:1.7.2' + testCompile 'org.apache.logging.log4j:log4j:2.4.1' + testCompile 'org.lable.rfc3881.auditlogger.adapter:slf4j:1.3' // http://joel-costigliola.github.io/assertj/ compile 'org.assertj:assertj-core:3.1.0' @@ -104,9 +109,18 @@ jar { instruction 'Bundle-DocURL', 'http://www.bjondinc.com' instruction 'Bundle-Activator', 'com.bjond.demo.SampleActivator' } + + // Include all of the dependencies. It explodes each jar file into class files and inserts the class files. - from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + doFirst { + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + } + + // The jose4j uses signed jars which will not match our manifest so I remove them. + // http://stackoverflow.com/a/14441628/3038736 + exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' + } @@ -154,6 +168,6 @@ task war() { } task all() { - dependsOn build, compileTestJava, findbugsMain, findbugsTest + dependsOn build, jar, compileTestJava, findbugsMain, findbugsTest } diff --git a/src/main/java/com/bjond/jwtutils/JWTUtil.java b/src/main/java/com/bjond/jwtutils/JWTUtil.java index 932a180..e004400 100644 --- a/src/main/java/com/bjond/jwtutils/JWTUtil.java +++ b/src/main/java/com/bjond/jwtutils/JWTUtil.java @@ -95,17 +95,17 @@ public static JwtClaims validateTokenAndProcessClaims(final Key key, /** - * Generates a JWT Token given a set of parameters common to JWT + * Convenience method that generates a JWT Token given a set of parameters common to JWT * implementations. * * @param bjondServerEncryptionKey * The Base64 encoded Encyrption key - * @param bjondAdapterSubject - * The indended Subject of the generated token - * @param bjondAdapterAudience - * The intended Audience of the generated token * @param issuer * The indended Issuer of the generated token + * @param bjondAdapterAudience + * The intended Audience of the generated token + * @param bjondAdapterSubject + * The indended Subject of the generated token * @param json * JSON snippet that will be inserted into the claim under the * key 'json' @@ -118,9 +118,9 @@ public static JwtClaims validateTokenAndProcessClaims(final Key key, * issue. */ public static String generateJWTToken(final String bjondServerEncryptionKey, - final String bjondAdapterSubject, - final String bjondAdapterAudience, final String issuer, + final String bjondAdapterAudience, + final String bjondAdapterSubject, final String json, final int expirationTimeMinutesInTheFuture) throws JoseException { diff --git a/src/test/java/com/bjond/test/TestJWTUtils.java b/src/test/java/com/bjond/test/TestJWTUtils.java index e230394..489adb2 100644 --- a/src/test/java/com/bjond/test/TestJWTUtils.java +++ b/src/test/java/com/bjond/test/TestJWTUtils.java @@ -14,9 +14,28 @@ package com.bjond.test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.StrictAssertions.assertThat; + +import java.security.Key; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.jose4j.base64url.Base64; +import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers; +import org.jose4j.jwe.JsonWebEncryption; +import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.keys.AesKey; +import org.jose4j.lang.ByteUtil; import org.junit.Assert; import org.junit.Test; +import com.bjond.jwtutils.JWTUtil; + /** JUnit Test Suite TestBrigid * * @version 0.001 10/16/15mport scala.ScalaHelloWorld; @@ -38,6 +57,181 @@ public void sanityCheck() throws Exception { System.out.println("This is a test"); // You should see this in the html report in stdout. } + + @Test + public void test_generateJWTToken() throws Exception { + final byte[] keyBytes = JWTUtil.generateRandomKey_AES128(); + final Key key = JWTUtil.generateAESKey(keyBytes); + + final String issuer = "Bjönd, Inc"; + final String audience = "Axis Health"; + final String subject = "Adapter Token"; + + final String token = JWTUtil.generateJWTToken(Base64.encode(keyBytes), issuer, audience, subject, "this is a test", 10); + final JwtClaims claims = JWTUtil.validateTokenAndProcessClaims(key, issuer, audience, subject, 30, token); + + + assertThat(claims).isNotNull(); + + final Map> claimsMap2 = claims.flattenClaims(); + assertThat(claimsMap2).isNotNull(); + assertThat(claimsMap2.get("json")).hasSize(1).contains("this is a test"); + } + + @Test + public void constructionAndVerification_JWE() throws Exception { + final String helloWorld = "Hello World!"; + + // https://bitbucket.org/b_c/jose4j/wiki/Home + // Basic JWE construction and verification. + final Key key = new AesKey(ByteUtil.randomBytes(16)); + JsonWebEncryption jwe = new JsonWebEncryption(); + jwe.setPayload(helloWorld); + jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128KW); + jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256); + jwe.setKey(key); + final String serializedJwe = jwe.getCompactSerialization(); + + + jwe = new JsonWebEncryption(); + jwe.setKey(key); + jwe.setCompactSerialization(serializedJwe); + + + assertThat(jwe.getPayload()).isEqualTo(helloWorld); + } + + @Test + public void produceAndConsume_JWE_JWT() throws Exception { + + //Create the Claims, which will be the content of the JWT + final JwtClaims claims = new JwtClaims(); + claims.setIssuer("Issuer"); // who creates the token and signs it + claims.setAudience("Audience"); // to whom the token is intended to be sent + claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now) + claims.setGeneratedJwtId(); // a unique identifier for the token + claims.setIssuedAtToNow(); // when the token was issued/created (now) + claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago) + claims.setSubject("subject"); // the subject/principal is whom the token is about + claims.setClaim("email","mail@example.com"); // additional claims/attributes about the subject can be added + List groups = Arrays.asList("group-one", "other-group", "group-three"); + claims.setStringListClaim("groups", groups); // multi-valued claims work too + + // JWE construction and insert into JWE thus producing JWE/JWT + final Key key = new AesKey(ByteUtil.randomBytes(16)); + JsonWebEncryption jwe = new JsonWebEncryption(); + jwe.setPayload(claims.toJson()); + jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128KW); + jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256); + jwe.setKey(key); + final String serializedJWT = jwe.getCompactSerialization(); + + + final JwtConsumer jwtConsumer = new JwtConsumerBuilder() + .setRequireExpirationTime() // the JWT must have an expiration time + .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew + .setRequireSubject() // the JWT must have a subject claim + .setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by + .setExpectedAudience("Audience") // to whom the JWT is intended for + .setDecryptionKey(key) + .setEnableRequireEncryption() + .setDisableRequireSignature() + .setSkipSignatureVerification() + .build(); // create the JwtConsumer instance + + // Validate the JWT and process it to the Claims + final JwtClaims jwtClaims = jwtConsumer.processToClaims(serializedJWT); + System.out.println("JWT validation succeeded! " + jwtClaims); + + // If it didn't blow up with an InvalidJwtException we are good! + } + + + + @Test + public void ensure_Base64_AES_256_Key_Works() throws Exception { + + //Create the Claims, which will be the content of the JWT + final JwtClaims claims = new JwtClaims(); + claims.setIssuer("Issuer"); // who creates the token and signs it + claims.setAudience("Audience"); // to whom the token is intended to be sent + claims.setExpirationTimeMinutesInTheFuture(10); // time when the token will expire (10 minutes from now) + claims.setGeneratedJwtId(); // a unique identifier for the token + claims.setIssuedAtToNow(); // when the token was issued/created (now) + claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago) + claims.setSubject("subject"); // the subject/principal is whom the token is about + claims.setClaim("email","mail@example.com"); // additional claims/attributes about the subject can be added + List groups = Arrays.asList("group-one", "other-group", "group-three"); + claims.setStringListClaim("groups", groups); // multi-valued claims work too + + // JWE construction and insert into JWE thus producing JWE/JWT + final byte[] AESKey = ByteUtil.randomBytes(16); + + ///////////////////////////////////////////////////////////////////////// + // This base64AESKeyString is the Base64 secrete key we will generate // + // and register with the Bjond System // + ///////////////////////////////////////////////////////////////////////// + final String base64AESKeyString = Base64.encode(AESKey); + final byte[] AESKeyDecoded = Base64.decode(base64AESKeyString); + + + final Key key = new AesKey(AESKeyDecoded); + JsonWebEncryption jwe = new JsonWebEncryption(); + jwe.setPayload(claims.toJson()); + jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128KW); + jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256); + jwe.setKey(key); + final String serializedJWT = jwe.getCompactSerialization(); + + + final JwtConsumer jwtConsumer = new JwtConsumerBuilder() + .setRequireExpirationTime() // the JWT must have an expiration time + .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew + .setRequireSubject() // the JWT must have a subject claim + .setExpectedIssuer("Issuer") // whom the JWT needs to have been issued by + .setExpectedAudience("Audience") // to whom the JWT is intended for + .setDecryptionKey(key) + .setEnableRequireEncryption() + .setDisableRequireSignature() + .setSkipSignatureVerification() + .build(); // create the JwtConsumer instance + + // Validate the JWT and process it to the Claims + final JwtClaims jwtClaims = jwtConsumer.processToClaims(serializedJWT); + System.out.println("JWT validation succeeded! " + jwtClaims); + + // If it didn't blow up with an InvalidJwtException we are good! + } + + + @Test + public void ensureAESKeyGeneration() throws Exception { + final byte[] AESKey = ByteUtil.randomBytes(16); // 128 Bit + final Key key = new AesKey(AESKey); + + + final String base64Key = Base64.encode(AESKey); + final byte[] key2 = Base64.decode(base64Key); + + assertThat(base64Key).isNotEmpty(); + assertThat(Arrays.equals(AESKey, key2)); + + + + // Just go through the motions, any exceptions? + JsonWebEncryption jwe = new JsonWebEncryption(); + jwe.setPayload("Hello World!"); + jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128KW); + jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256); + jwe.setKey(key); + final String serializedJwe = jwe.getCompactSerialization(); + jwe = new JsonWebEncryption(); + jwe.setKey(key); + jwe.setCompactSerialization(serializedJwe); + + + + } }