Skip to content

Commit

Permalink
Merge branch 'release-1.3.1'
Browse files Browse the repository at this point in the history
Conflicts:
	build.gradle
  • Loading branch information
tmackenzie committed Aug 10, 2018
2 parents c0c99c6 + 996994f commit 3fee328
Show file tree
Hide file tree
Showing 19 changed files with 578 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
!gradle-wrapper.jar
.gradle
/build/
/out/

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
Expand Down
88 changes: 84 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,91 @@
[JSON Web Tokens](https://tools.ietf.org/html/rfc7519)
---------------------------------------------------------------------------------------------------------------------

[![Build Status](https://travis-ci.org/RootServices/jwt.svg?branch=development)](https://travis-ci.org/RootServices/jwt)


Documentation
------------
Documentation is written in [github pages](http://rootservices.github.io/jwt/). Which is located in the branch, [gh-pages](https://github.com/RootServices/jwt/tree/gh-pages)
More documentation is available [here](http://rootservices.github.io/jwt/).

Quick Start
-----------
This is a Java implementation of JWT, JWS, and JWE.

## Unsecured JWT
```java
UnsecureCompactBuilder compactBuilder = new UnsecureCompactBuilder();

Claim claim = new Claim();
claim.setUriIsRoot(true);

ByteArrayOutputStream encodedJwt = compactBuilder.claims(claim).build();
```

## JWS Compact Serialization

### Asymmetric key
```java
SecureCompactBuilder compactBuilder = new SecureCompactBuilder();

RSAKeyPair key = Factory.makeRSAKeyPair();
key.setKeyId(Optional.of("test-key-id"));

Claim claim = new Claim();
claim.setUriIsRoot(true);

ByteArrayOutputStream actual = subject.alg(Algorithm.RS256)
.key(key)
.claims(claim)
.build();
```

### Symmetric key
```java
SecureCompactBuilder compactBuilder = new SecureCompactBuilder();

SymmetricKey key = Factory.makeSymmetricKey();
key.setKeyId(Optional.of("test-key-id"));

Claim claim = new Claim();
claim.setUriIsRoot(true);

ByteArrayOutputStream actual = compactBuilder.alg(Algorithm.HS256)
.key(key)
.claims(claim)
.build();
```

## JWE Compact Serialization

### Asymmetric key
```java
EncryptedCompactBuilder compactBuilder = new EncryptedCompactBuilder();

byte[] payload = "Help me, Obi-Wan Kenobi. You're my only hope.".getBytes();

RSAPublicKey publicKey = Factory.makeRSAPublicKeyForJWE();
publicKey.setKeyId(Optional.of(UUID.randomUUID().toString()));

ByteArrayOutputStream actual = compactBuilder.encAlg(EncryptionAlgorithm.AES_GCM_256)
.alg(Algorithm.RSAES_OAEP)
.payload(payload)
.rsa(publicKey)
.build();
```

### Symmetric key
```java
EncryptedCompactBuilder compactBuilder = new EncryptedCompactBuilder();

SymmetricKey key = Factory.makeSymmetricKeyForJWE();

byte[] payload = "Help me, Obi-Wan Kenobi. You're my only hope.".getBytes();

Travis Results
-------------------------------------
- development branch [![Build Status](https://travis-ci.org/RootServices/jwt.svg?branch=development)](https://travis-ci.org/RootServices/jwt)
ByteArrayOutputStream actual = compactBuilder.encAlg(EncryptionAlgorithm.AES_GCM_256)
.alg(Algorithm.DIRECT)
.encAlg(EncryptionAlgorithm.AES_GCM_256)
.payload(payload)
.cek(key)
.build();
```
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apply plugin: 'signing'
apply plugin: 'maven'

group = "org.rootservices"
version = "1.3"
version = "1.3.1"
archivesBaseName="jwt"

description = """JSON Web Tokens"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package org.rootservices.jwt.builder.compact;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.rootservices.jwt.builder.exception.CompactException;
import org.rootservices.jwt.config.JwtAppFactory;
import org.rootservices.jwt.entity.jwe.EncryptionAlgorithm;
import org.rootservices.jwt.entity.jwk.RSAPublicKey;
import org.rootservices.jwt.entity.jwk.SymmetricKey;
import org.rootservices.jwt.entity.jwt.header.Algorithm;
import org.rootservices.jwt.entity.jwt.header.Header;
import org.rootservices.jwt.jwe.entity.JWE;
import org.rootservices.jwt.jwe.factory.exception.CipherException;
import org.rootservices.jwt.jwe.serialization.JweSerializer;
import org.rootservices.jwt.jws.signer.factory.rsa.exception.PublicKeyException;
import org.rootservices.jwt.serialization.exception.EncryptException;
import org.rootservices.jwt.serialization.exception.JsonToJwtException;

import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.Optional;

public class EncryptedCompactBuilder {
private static final Logger LOGGER = LogManager.getLogger(EncryptedCompactBuilder.class);
public static final String UNABLE_TO_BUILD_COMPACT_JWE = "Unable to build compact jwe";
private static JwtAppFactory jwtAppFactory = new JwtAppFactory();

private byte[] payload;
private SymmetricKey cek;
private Algorithm alg;
private EncryptionAlgorithm encAlg;

private RSAPublicKey publicKey;

public EncryptedCompactBuilder payload(byte[] payload) {
this.payload = payload;
return this;
}

public EncryptedCompactBuilder rsa(RSAPublicKey publicKey) {
this.publicKey = publicKey;
return this;
}

public EncryptedCompactBuilder cek(SymmetricKey cek) {
this.cek = cek;
return this;
}

public EncryptedCompactBuilder alg(Algorithm alg) {
this.alg = alg;
return this;
}

public EncryptedCompactBuilder encAlg(EncryptionAlgorithm encAlg) {
this.encAlg = encAlg;
return this;
}

public ByteArrayOutputStream build() throws CompactException {
JWE jwe = jwe();
JweSerializer jweSerializer = jweSerializer();

try {
return jweSerializer.JWEToCompact(jwe);
} catch (JsonToJwtException | CipherException | EncryptException e) {
LOGGER.error(e.getMessage(), e);
throw new CompactException(UNABLE_TO_BUILD_COMPACT_JWE, e);
}
}

// a few factory methods to help the build method.

protected JweSerializer jweSerializer() throws CompactException {
JweSerializer jweSerializer;
if (publicKey != null) {
jweSerializer = jweRsaSerializer();
} else {
jweSerializer = jwtAppFactory.jweDirectSerializer();
}
return jweSerializer;
}

protected JweSerializer jweRsaSerializer() throws CompactException {
try {
return jwtAppFactory.jweRsaSerializer(publicKey);
} catch (PublicKeyException | CipherException e) {
LOGGER.error(e.getMessage(), e);
throw new CompactException(UNABLE_TO_BUILD_COMPACT_JWE, e);
}
}

protected JWE jwe() {
if (publicKey != null) {
return jweForRsa();
} else {
return jweForDirect();
}
}

protected JWE jweForDirect() {
Base64.Decoder decoder = jwtAppFactory.urlDecoder();

JWE jwe = new JWE();
Header header = new Header();

header.setEncryptionAlgorithm(Optional.of(this.encAlg));
header.setAlgorithm(this.alg);
header.setKeyId(cek.getKeyId());

jwe.setHeader(header);
jwe.setPayload(payload);
jwe.setCek(decoder.decode(cek.getKey()));

return jwe;
}

protected JWE jweForRsa() {
JWE jwe = new JWE();
Header header = new Header();

header.setKeyId(publicKey.getKeyId());
header.setEncryptionAlgorithm(Optional.of(this.encAlg));
header.setAlgorithm(this.alg);

jwe.setHeader(header);
jwe.setPayload(payload);

return jwe;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.rootservices.jwt.builder.compact;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.rootservices.jwt.builder.exception.CompactException;
import org.rootservices.jwt.config.JwtAppFactory;
import org.rootservices.jwt.entity.jwk.Key;
import org.rootservices.jwt.entity.jwt.Claims;
import org.rootservices.jwt.entity.jwt.header.Algorithm;
import org.rootservices.jwt.exception.SignatureException;
import org.rootservices.jwt.jws.serialization.SecureJwtSerializer;
import org.rootservices.jwt.serialization.exception.JwtToJsonException;

import java.io.ByteArrayOutputStream;

public class SecureCompactBuilder {
private static final Logger LOGGER = LogManager.getLogger(SecureCompactBuilder.class);
public static final String UNABLE_TO_BUILD_COMPACT_JWT = "Unable to build compact jwt";
private static JwtAppFactory jwtAppFactory = new JwtAppFactory();

private Claims claims;
private Key key;
private Algorithm alg;

public SecureCompactBuilder claims(Claims claims) {
this.claims = claims;
return this;
}

public SecureCompactBuilder key(Key key) {
this.key = key;
return this;
}

public SecureCompactBuilder alg(Algorithm alg) {
this.alg = alg;
return this;
}


public ByteArrayOutputStream build() throws CompactException {
SecureJwtSerializer secureJwtSerializer;
try {
secureJwtSerializer = jwtAppFactory.secureJwtSerializer(alg, key);
} catch (SignatureException e) {
LOGGER.error(e.getMessage(), e);
throw new CompactException(UNABLE_TO_BUILD_COMPACT_JWT, e);
}

try {
return secureJwtSerializer.compactJwt(claims);
} catch (JwtToJsonException e) {
LOGGER.error(e.getMessage(), e);
throw new CompactException(UNABLE_TO_BUILD_COMPACT_JWT, e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.rootservices.jwt.builder.compact;

import org.rootservices.jwt.config.JwtAppFactory;
import org.rootservices.jwt.entity.jwt.Claims;
import org.rootservices.jwt.serialization.UnSecureJwtSerializer;

import java.io.ByteArrayOutputStream;

public class UnsecureCompactBuilder {
private static JwtAppFactory jwtAppFactory = new JwtAppFactory();
private Claims claims;

public UnsecureCompactBuilder claims(Claims claims) {
this.claims = claims;
return this;
}

public ByteArrayOutputStream build() {
UnSecureJwtSerializer unSecureJwtSerializer = jwtAppFactory.unSecureJwtSerializer();
return unSecureJwtSerializer.compactJwt(claims);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.rootservices.jwt.builder.exception;

public class CompactException extends Exception {
public CompactException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.rootservices.jwt.jwe.factory.CipherSymmetricFactory;
import org.rootservices.jwt.jwe.factory.exception.CipherException;
import org.rootservices.jwt.jwe.serialization.JweDeserializer;
import org.rootservices.jwt.jwe.serialization.exception.KeyException;
import org.rootservices.jwt.serialization.Serdes;
import org.rootservices.jwt.serialization.exception.DecryptException;
import org.rootservices.jwt.serialization.exception.JsonException;
Expand All @@ -21,6 +22,7 @@


public class JweDirectDesializer implements JweDeserializer {
public static final String KEY_CANNOT_BE_DECODED = "Key cannot be decoded.";
private Serdes serdes;
private Base64.Decoder decoder;
private CipherSymmetricFactory cipherSymmetricFactory;
Expand All @@ -32,7 +34,7 @@ public JweDirectDesializer(Serdes serdes, Base64.Decoder decoder, CipherSymmetri
}

@Override
public JWE stringToJWE(String compactJWE, Key key) throws JsonToJwtException, DecryptException, CipherException {
public JWE stringToJWE(String compactJWE, Key key) throws JsonToJwtException, DecryptException, CipherException, KeyException {
String[] jweParts = compactJWE.split(JWT_SPLITTER);
byte[] protectedHeader = decoder.decode(jweParts[0]);
byte[] initVector = decoder.decode(jweParts[2]);
Expand All @@ -48,7 +50,13 @@ public JWE stringToJWE(String compactJWE, Key key) throws JsonToJwtException, De

byte[] aad = jweParts[0].getBytes(StandardCharsets.US_ASCII);

byte[] cek = decoder.decode(((SymmetricKey) key).getKey().getBytes());
byte[] cek;
try {
cek = decoder.decode(((SymmetricKey) key).getKey().getBytes());
} catch (IllegalArgumentException e) {
throw new KeyException(KEY_CANNOT_BE_DECODED, e);
}

Cipher symmetricCipher;
try {
symmetricCipher = cipherSymmetricFactory.forDecrypt(Transformation.AES_GCM_NO_PADDING, cek, initVector, aad);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

public class HeaderDeserializer {
public static final String JWT_SPLITTER = "\\.";
public final int JWT_LENGTH = 2;
public static final String INVALID_HEADER = "JOSE Header is invalid";
public static final String JWT_IS_NOT_SPLITTABLE = "JWT is not splittable by '.'";
private Base64.Decoder decoder;
Expand All @@ -22,7 +23,7 @@ public HeaderDeserializer(Base64.Decoder decoder, Serdes serdes) {
public Header toHeader(String encodedJwt) throws JsonToJwtException, InvalidJWT {
String[] jwtParts = encodedJwt.split(JWT_SPLITTER);

if (jwtParts.length == 0) {
if (jwtParts.length < JWT_LENGTH) {
throw new InvalidJWT(JWT_IS_NOT_SPLITTABLE);
}

Expand Down
Loading

0 comments on commit 3fee328

Please sign in to comment.