Re-arranged packages in the JWT library; added an implementation of the

ES256 signature method (untested) and a stub for the RE256 signature
method.
pull/59/head
Amanda Anganes 2011-12-28 21:26:11 -05:00
parent 204df8f0bd
commit 15f8675e1a
11 changed files with 1053 additions and 938 deletions

View File

@ -1,155 +1,155 @@
package org.mitre.jwt; package org.mitre.jwt.model;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.List; import java.util.List;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
public class Jwt { public class Jwt {
private JwtHeader header; private JwtHeader header;
private JwtClaims claims; private JwtClaims claims;
/** /**
* Base64Url encoded signature string * Base64Url encoded signature string
*/ */
private String signature; private String signature;
public Jwt() { public Jwt() {
this.header = new JwtHeader(); this.header = new JwtHeader();
this.claims = new JwtClaims(); this.claims = new JwtClaims();
this.signature = null; // unsigned by default this.signature = null; // unsigned by default
} }
/** /**
* Create a Jwt from existing components * Create a Jwt from existing components
* @param header * @param header
* @param claims * @param claims
* @param signature * @param signature
*/ */
public Jwt(JwtHeader header, JwtClaims claims, String signature) { public Jwt(JwtHeader header, JwtClaims claims, String signature) {
super(); super();
this.header = header; this.header = header;
this.claims = claims; this.claims = claims;
this.signature = signature; this.signature = signature;
} }
/** /**
* @return the header * @return the header
*/ */
public JwtHeader getHeader() { public JwtHeader getHeader() {
return header; return header;
} }
/** /**
* @param header the header to set * @param header the header to set
*/ */
public void setHeader(JwtHeader header) { public void setHeader(JwtHeader header) {
this.header = header; this.header = header;
} }
/** /**
* @return the claims * @return the claims
*/ */
public JwtClaims getClaims() { public JwtClaims getClaims() {
return claims; return claims;
} }
/** /**
* @param claims the claims to set * @param claims the claims to set
*/ */
public void setClaims(JwtClaims claims) { public void setClaims(JwtClaims claims) {
this.claims = claims; this.claims = claims;
} }
/** /**
* @return the signature * @return the signature
*/ */
public String getSignature() { public String getSignature() {
return signature; return signature;
} }
/** /**
* @param signature the signature to set * @param signature the signature to set
*/ */
public void setSignature(String signature) { public void setSignature(String signature) {
this.signature = signature; this.signature = signature;
} }
/** /**
* Return the canonical encoded string of this JWT, the header in Base64, a period ".", the claims in Base64, a period ".", and the signature in Base64. * Return the canonical encoded string of this JWT, the header in Base64, a period ".", the claims in Base64, a period ".", and the signature in Base64.
*/ */
public String toString() { public String toString() {
return getSignatureBase() + "." + Strings.nullToEmpty(this.signature); return getSignatureBase() + "." + Strings.nullToEmpty(this.signature);
} }
/** /**
* The signature base of a JWT is the header in Base64, a period ".", and the claims in Base64. * The signature base of a JWT is the header in Base64, a period ".", and the claims in Base64.
*/ */
public String getSignatureBase() { public String getSignatureBase() {
JsonObject h = header.getAsJsonObject(); JsonObject h = header.getAsJsonObject();
JsonObject c = claims.getAsJsonObject(); JsonObject c = claims.getAsJsonObject();
String h64 = new String(Base64.encodeBase64URLSafe(h.toString().getBytes())); String h64 = new String(Base64.encodeBase64URLSafe(h.toString().getBytes()));
String c64 = new String(Base64.encodeBase64URLSafe(c.toString().getBytes())); String c64 = new String(Base64.encodeBase64URLSafe(c.toString().getBytes()));
return h64 + "." + c64; return h64 + "." + c64;
} }
/** /**
* Parse a wire-encoded JWT * Parse a wire-encoded JWT
*/ */
public static Jwt parse(String s) { public static Jwt parse(String s) {
// split on the dots // split on the dots
List<String> parts = Lists.newArrayList(Splitter.on(".").split(s)); List<String> parts = Lists.newArrayList(Splitter.on(".").split(s));
if (parts.size() != 3) { if (parts.size() != 3) {
throw new IllegalArgumentException("Invalid JWT format."); throw new IllegalArgumentException("Invalid JWT format.");
} }
String h64 = parts.get(0); String h64 = parts.get(0);
String c64 = parts.get(1); String c64 = parts.get(1);
String s64 = parts.get(2); String s64 = parts.get(2);
JsonParser parser = new JsonParser(); JsonParser parser = new JsonParser();
JsonObject hjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(h64)))).getAsJsonObject(); JsonObject hjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(h64)))).getAsJsonObject();
JsonObject cjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(c64)))).getAsJsonObject(); JsonObject cjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(c64)))).getAsJsonObject();
// shuttle for return value // shuttle for return value
Jwt jwt = new Jwt(new JwtHeader(hjo), new JwtClaims(cjo), s64); Jwt jwt = new Jwt(new JwtHeader(hjo), new JwtClaims(cjo), s64);
// TODO: save the wire-encoded string in the Jwt object itself? // TODO: save the wire-encoded string in the Jwt object itself?
return jwt; return jwt;
} }
} }

View File

@ -1,292 +1,292 @@
package org.mitre.jwt; package org.mitre.jwt.model;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
public class JwtClaims { public class JwtClaims {
/** /**
* ISO8601 / RFC3339 Date Format * ISO8601 / RFC3339 Date Format
*/ */
public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"); public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
/* /*
* TODO: Should we instead be using a generic claims map with well-named accessor methods? * TODO: Should we instead be using a generic claims map with well-named accessor methods?
*/ */
private Date expiration; private Date expiration;
private Date notBefore; private Date notBefore;
private Date issuedAt; private Date issuedAt;
private String issuer; private String issuer;
private String audience; private String audience;
private String principal; private String principal;
private String jwtId; private String jwtId;
private String type; private String type;
private Map<String, Object> claims = new HashMap<String, Object>(); private Map<String, Object> claims = new HashMap<String, Object>();
public JwtClaims() { public JwtClaims() {
} }
public JwtClaims(JsonObject json) { public JwtClaims(JsonObject json) {
for (Entry<String, JsonElement> element : json.entrySet()) { for (Entry<String, JsonElement> element : json.entrySet()) {
if (element.getKey().equals("exp")) { if (element.getKey().equals("exp")) {
expiration = new Date(element.getValue().getAsLong() * 1000L); expiration = new Date(element.getValue().getAsLong() * 1000L);
} else if (element.getKey().equals("nbf")) { } else if (element.getKey().equals("nbf")) {
notBefore = new Date(element.getValue().getAsLong() * 1000L); notBefore = new Date(element.getValue().getAsLong() * 1000L);
} else if (element.getKey().equals("iat")) { } else if (element.getKey().equals("iat")) {
issuedAt = new Date(element.getValue().getAsLong() * 1000L); issuedAt = new Date(element.getValue().getAsLong() * 1000L);
} else if (element.getKey().equals("iss")) { } else if (element.getKey().equals("iss")) {
issuer = element.getValue().getAsString(); issuer = element.getValue().getAsString();
} else if (element.getKey().equals("aud")) { } else if (element.getKey().equals("aud")) {
audience = element.getValue().getAsString(); audience = element.getValue().getAsString();
} else if (element.getKey().equals("prn")) { } else if (element.getKey().equals("prn")) {
principal = element.getValue().getAsString(); principal = element.getValue().getAsString();
} else if (element.getKey().equals("jti")) { } else if (element.getKey().equals("jti")) {
jwtId = element.getValue().getAsString(); jwtId = element.getValue().getAsString();
} else if (element.getKey().equals("typ")) { } else if (element.getKey().equals("typ")) {
type = element.getValue().getAsString(); type = element.getValue().getAsString();
} else if (element.getValue().isJsonPrimitive()){ } else if (element.getValue().isJsonPrimitive()){
// we handle all primitives in here // we handle all primitives in here
JsonPrimitive prim = element.getValue().getAsJsonPrimitive(); JsonPrimitive prim = element.getValue().getAsJsonPrimitive();
if (prim.isBoolean()) { if (prim.isBoolean()) {
claims.put(element.getKey(), prim.getAsBoolean()); claims.put(element.getKey(), prim.getAsBoolean());
} else if (prim.isNumber()) { } else if (prim.isNumber()) {
claims.put(element.getKey(), prim.getAsNumber()); claims.put(element.getKey(), prim.getAsNumber());
} else if (prim.isString()) { } else if (prim.isString()) {
claims.put(element.getKey(), prim.getAsString()); claims.put(element.getKey(), prim.getAsString());
} }
} else { } else {
// everything else gets handled as a raw JsonElement // everything else gets handled as a raw JsonElement
claims.put(element.getKey(), element.getValue()); claims.put(element.getKey(), element.getValue());
} }
} }
} }
/** /**
* @return the expiration * @return the expiration
*/ */
public Date getExpiration() { public Date getExpiration() {
return expiration; return expiration;
} }
/** /**
* @param expiration the expiration to set * @param expiration the expiration to set
*/ */
public void setExpiration(Date expiration) { public void setExpiration(Date expiration) {
this.expiration = expiration; this.expiration = expiration;
} }
/** /**
* @return the notBefore * @return the notBefore
*/ */
public Date getNotBefore() { public Date getNotBefore() {
return notBefore; return notBefore;
} }
/** /**
* @param notBefore the notBefore to set * @param notBefore the notBefore to set
*/ */
public void setNotBefore(Date notBefore) { public void setNotBefore(Date notBefore) {
this.notBefore = notBefore; this.notBefore = notBefore;
} }
/** /**
* @return the issuedAt * @return the issuedAt
*/ */
public Date getIssuedAt() { public Date getIssuedAt() {
return issuedAt; return issuedAt;
} }
/** /**
* @param issuedAt the issuedAt to set * @param issuedAt the issuedAt to set
*/ */
public void setIssuedAt(Date issuedAt) { public void setIssuedAt(Date issuedAt) {
this.issuedAt = issuedAt; this.issuedAt = issuedAt;
} }
/** /**
* @return the issuer * @return the issuer
*/ */
public String getIssuer() { public String getIssuer() {
return issuer; return issuer;
} }
/** /**
* @param issuer the issuer to set * @param issuer the issuer to set
*/ */
public void setIssuer(String issuer) { public void setIssuer(String issuer) {
this.issuer = issuer; this.issuer = issuer;
} }
/** /**
* @return the audience * @return the audience
*/ */
public String getAudience() { public String getAudience() {
return audience; return audience;
} }
/** /**
* @param audience the audience to set * @param audience the audience to set
*/ */
public void setAudience(String audience) { public void setAudience(String audience) {
this.audience = audience; this.audience = audience;
} }
/** /**
* @return the principal * @return the principal
*/ */
public String getPrincipal() { public String getPrincipal() {
return principal; return principal;
} }
/** /**
* @param principal the principal to set * @param principal the principal to set
*/ */
public void setPrincipal(String principal) { public void setPrincipal(String principal) {
this.principal = principal; this.principal = principal;
} }
/** /**
* @return the jwtId * @return the jwtId
*/ */
public String getJwtId() { public String getJwtId() {
return jwtId; return jwtId;
} }
/** /**
* @param jwtId the jwtId to set * @param jwtId the jwtId to set
*/ */
public void setJwtId(String jwtId) { public void setJwtId(String jwtId) {
this.jwtId = jwtId; this.jwtId = jwtId;
} }
/** /**
* @return the type * @return the type
*/ */
public String getType() { public String getType() {
return type; return type;
} }
/** /**
* @param type the type to set * @param type the type to set
*/ */
public void setType(String type) { public void setType(String type) {
this.type = type; this.type = type;
} }
/** /**
* Get an extension claim * Get an extension claim
*/ */
public Object getClaim(String key) { public Object getClaim(String key) {
return claims.get(key); return claims.get(key);
} }
/** /**
* Set an extension claim * Set an extension claim
*/ */
public void setClaim(String key, Object value) { public void setClaim(String key, Object value) {
claims.put(key, value); claims.put(key, value);
} }
/** /**
* Remove an extension claim * Remove an extension claim
*/ */
public Object removeClaim(String key) { public Object removeClaim(String key) {
return claims.remove(key); return claims.remove(key);
} }
/** /**
* Get a copy of this header as a JsonObject. The JsonObject is not * Get a copy of this header as a JsonObject. The JsonObject is not
* backed by a live copy of this JwtHeader. * backed by a live copy of this JwtHeader.
* @return a copy of the data in this header in a JsonObject * @return a copy of the data in this header in a JsonObject
*/ */
public JsonObject getAsJsonObject() { public JsonObject getAsJsonObject() {
JsonObject o = new JsonObject(); JsonObject o = new JsonObject();
if (this.expiration != null) { if (this.expiration != null) {
o.addProperty("exp", this.expiration.getTime() / 1000L); o.addProperty("exp", this.expiration.getTime() / 1000L);
} }
if (this.notBefore != null) { if (this.notBefore != null) {
o.addProperty("nbf", this.notBefore.getTime() / 1000L); o.addProperty("nbf", this.notBefore.getTime() / 1000L);
} }
if (this.issuedAt != null) { if (this.issuedAt != null) {
o.addProperty("iat", this.issuedAt.getTime() / 1000L); o.addProperty("iat", this.issuedAt.getTime() / 1000L);
} }
if (this.issuer != null) { if (this.issuer != null) {
o.addProperty("iss", this.issuer); o.addProperty("iss", this.issuer);
} }
if (this.audience != null) { if (this.audience != null) {
o.addProperty("aud", this.audience); o.addProperty("aud", this.audience);
} }
if (this.principal != null) { if (this.principal != null) {
o.addProperty("prn", this.principal); o.addProperty("prn", this.principal);
} }
if (this.jwtId != null) { if (this.jwtId != null) {
o.addProperty("jti", this.jwtId); o.addProperty("jti", this.jwtId);
} }
if (this.type != null) { if (this.type != null) {
o.addProperty("typ", this.type); o.addProperty("typ", this.type);
} }
if (this.claims != null) { if (this.claims != null) {
for (Map.Entry<String, Object> claim : this.claims.entrySet()) { for (Map.Entry<String, Object> claim : this.claims.entrySet()) {
if (claim.getValue() instanceof JsonElement) { if (claim.getValue() instanceof JsonElement) {
o.add(claim.getKey(), (JsonElement)claim.getValue()); o.add(claim.getKey(), (JsonElement)claim.getValue());
} else if (claim.getValue() instanceof String) { } else if (claim.getValue() instanceof String) {
o.addProperty(claim.getKey(), (String)claim.getValue()); o.addProperty(claim.getKey(), (String)claim.getValue());
} else if (claim.getValue() instanceof Number) { } else if (claim.getValue() instanceof Number) {
o.addProperty(claim.getKey(), (Number)claim.getValue()); o.addProperty(claim.getKey(), (Number)claim.getValue());
} else if (claim.getValue() instanceof Boolean) { } else if (claim.getValue() instanceof Boolean) {
o.addProperty(claim.getKey(), (Boolean)claim.getValue()); o.addProperty(claim.getKey(), (Boolean)claim.getValue());
} else if (claim.getValue() instanceof Character) { } else if (claim.getValue() instanceof Character) {
o.addProperty(claim.getKey(), (Character)claim.getValue()); o.addProperty(claim.getKey(), (Character)claim.getValue());
} else if (claim.getValue() != null) { } else if (claim.getValue() != null) {
// try to put it in as a string // try to put it in as a string
o.addProperty(claim.getKey(), claim.getValue().toString()); o.addProperty(claim.getKey(), claim.getValue().toString());
} else { } else {
// otherwise add in as a null // otherwise add in as a null
o.add(claim.getKey(), null); o.add(claim.getKey(), null);
} }
} }
} }
return o; return o;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Object#toString() * @see java.lang.Object#toString()
*/ */
@Override @Override
public String toString() { public String toString() {
return "JwtClaims [expiration=" + expiration + ", notBefore=" + notBefore + ", issuedAt=" + issuedAt + ", issuer=" + issuer + ", audience=" + audience + ", principal=" + principal + ", jwtId=" + jwtId + ", type=" + type + ", claims=" + claims + "]"; return "JwtClaims [expiration=" + expiration + ", notBefore=" + notBefore + ", issuedAt=" + issuedAt + ", issuer=" + issuer + ", audience=" + audience + ", principal=" + principal + ", jwtId=" + jwtId + ", type=" + type + ", claims=" + claims + "]";
} }
} }

View File

@ -1,172 +1,172 @@
package org.mitre.jwt; package org.mitre.jwt.model;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
public class JwtHeader { public class JwtHeader {
/* /*
* TODO: Should we instead be using a generic claims map with well-named accessor methods? * TODO: Should we instead be using a generic claims map with well-named accessor methods?
*/ */
private String type; private String type;
private String algorithm; private String algorithm;
private String encryptionMethod; private String encryptionMethod;
private Map<String, Object> claims = new HashMap<String, Object>(); private Map<String, Object> claims = new HashMap<String, Object>();
/** /**
* Make an empty header * Make an empty header
*/ */
public JwtHeader() { public JwtHeader() {
} }
/** /**
* Build a header from a JSON object * Build a header from a JSON object
* @param json * @param json
*/ */
public JwtHeader(JsonObject json) { public JwtHeader(JsonObject json) {
for (Entry<String, JsonElement> element : json.entrySet()) { for (Entry<String, JsonElement> element : json.entrySet()) {
if (element.getKey().equals("typ")) { if (element.getKey().equals("typ")) {
this.type = json.get("typ").getAsString(); this.type = json.get("typ").getAsString();
} else if (element.getKey().equals("alg")) { } else if (element.getKey().equals("alg")) {
this.algorithm = json.get("alg").getAsString(); this.algorithm = json.get("alg").getAsString();
} else if (element.getKey().equals("enc")) { } else if (element.getKey().equals("enc")) {
this.encryptionMethod = json.get("enc").getAsString(); this.encryptionMethod = json.get("enc").getAsString();
} else { } else {
// TODO: this assumes string encoding for extensions, probably not quite correct // TODO: this assumes string encoding for extensions, probably not quite correct
claims.put(element.getKey(), element.getValue().getAsString()); claims.put(element.getKey(), element.getValue().getAsString());
} }
} }
} }
/** /**
* @return the type * @return the type
*/ */
public String getType() { public String getType() {
return type; return type;
} }
/** /**
* @param type the type to set * @param type the type to set
*/ */
public void setType(String type) { public void setType(String type) {
this.type = type; this.type = type;
} }
/** /**
* @return the algorithm * @return the algorithm
*/ */
public String getAlgorithm() { public String getAlgorithm() {
return algorithm; return algorithm;
} }
/** /**
* @param algorithm the algorithm to set * @param algorithm the algorithm to set
*/ */
public void setAlgorithm(String algorithm) { public void setAlgorithm(String algorithm) {
this.algorithm = algorithm; this.algorithm = algorithm;
} }
/** /**
* @return the encryptionMethod * @return the encryptionMethod
*/ */
public String getEncryptionMethod() { public String getEncryptionMethod() {
return encryptionMethod; return encryptionMethod;
} }
/** /**
* @param encryptionMethod the encryptionMethod to set * @param encryptionMethod the encryptionMethod to set
*/ */
public void setEncryptionMethod(String encryptionMethod) { public void setEncryptionMethod(String encryptionMethod) {
this.encryptionMethod = encryptionMethod; this.encryptionMethod = encryptionMethod;
} }
/** /**
* Get an extension claim * Get an extension claim
*/ */
public Object getClaim(String key) { public Object getClaim(String key) {
return claims.get(key); return claims.get(key);
} }
/** /**
* Set an extension claim * Set an extension claim
*/ */
public void setClaim(String key, Object value) { public void setClaim(String key, Object value) {
claims.put(key, value); claims.put(key, value);
} }
/** /**
* Remove an extension claim * Remove an extension claim
*/ */
public Object removeClaim(String key) { public Object removeClaim(String key) {
return claims.remove(key); return claims.remove(key);
} }
/** /**
* Get a copy of this header as a JsonObject. The JsonObject is not * Get a copy of this header as a JsonObject. The JsonObject is not
* backed by a live copy of this JwtHeader. * backed by a live copy of this JwtHeader.
* @return a copy of the data in this header in a JsonObject * @return a copy of the data in this header in a JsonObject
*/ */
public JsonObject getAsJsonObject() { public JsonObject getAsJsonObject() {
JsonObject o = new JsonObject(); JsonObject o = new JsonObject();
if (this.type != null) { if (this.type != null) {
o.addProperty("typ", this.type); o.addProperty("typ", this.type);
} }
if (this.algorithm != null) { if (this.algorithm != null) {
o.addProperty("alg", this.algorithm); o.addProperty("alg", this.algorithm);
} }
if (this.encryptionMethod != null) { if (this.encryptionMethod != null) {
o.addProperty("enc", this.encryptionMethod); o.addProperty("enc", this.encryptionMethod);
} }
if (this.claims != null) { if (this.claims != null) {
for (Map.Entry<String, Object> claim : this.claims.entrySet()) { for (Map.Entry<String, Object> claim : this.claims.entrySet()) {
if (claim.getValue() instanceof String) { if (claim.getValue() instanceof String) {
o.addProperty(claim.getKey(), (String)claim.getValue()); o.addProperty(claim.getKey(), (String)claim.getValue());
} else if (claim.getValue() instanceof Number) { } else if (claim.getValue() instanceof Number) {
o.addProperty(claim.getKey(), (Number)claim.getValue()); o.addProperty(claim.getKey(), (Number)claim.getValue());
} else if (claim.getValue() instanceof Boolean) { } else if (claim.getValue() instanceof Boolean) {
o.addProperty(claim.getKey(), (Boolean)claim.getValue()); o.addProperty(claim.getKey(), (Boolean)claim.getValue());
} else if (claim.getValue() instanceof Character) { } else if (claim.getValue() instanceof Character) {
o.addProperty(claim.getKey(), (Character)claim.getValue()); o.addProperty(claim.getKey(), (Character)claim.getValue());
} else if (claim.getValue() != null) { } else if (claim.getValue() != null) {
// try to put it in as a string // try to put it in as a string
o.addProperty(claim.getKey(), claim.getValue().toString()); o.addProperty(claim.getKey(), claim.getValue().toString());
} else { } else {
// otherwise add in as a null // otherwise add in as a null
o.add(claim.getKey(), null); o.add(claim.getKey(), null);
} }
} }
} }
return o; return o;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Object#toString() * @see java.lang.Object#toString()
*/ */
@Override @Override
public String toString() { public String toString() {
return "JwtHeader [type=" + type + ", algorithm=" + algorithm + ", encryptionMethod=" + encryptionMethod + ", claims=" + claims + "]"; return "JwtHeader [type=" + type + ", algorithm=" + algorithm + ", encryptionMethod=" + encryptionMethod + ", claims=" + claims + "]";
} }
} }

View File

@ -1,80 +1,82 @@
package org.mitre.jwt; package org.mitre.jwt.signer;
import java.util.List; import java.util.List;
import com.google.common.base.Objects; import org.mitre.jwt.model.Jwt;
import com.google.common.base.Splitter;
import com.google.common.base.Strings; import com.google.common.base.Objects;
import com.google.common.collect.Lists; import com.google.common.base.Splitter;
import com.google.common.base.Strings;
public abstract class AbstractJwtSigner implements JwtSigner { import com.google.common.collect.Lists;
public static final String PLAINTEXT = "none"; public abstract class AbstractJwtSigner implements JwtSigner {
public static final String HS256 = "HS256";
public static final String HS384 = "HS384"; public static final String PLAINTEXT = "none";
public static final String HS512 = "HS512"; public static final String HS256 = "HS256";
public static final String HS384 = "HS384";
private String algorithm; public static final String HS512 = "HS512";
public AbstractJwtSigner(String algorithm) { private String algorithm;
this.algorithm = algorithm;
} public AbstractJwtSigner(String algorithm) {
this.algorithm = algorithm;
/** }
* @return the algorithm
*/ /**
public String getAlgorithm() { * @return the algorithm
return algorithm; */
} public String getAlgorithm() {
return algorithm;
/** }
* @param algorithm the algorithm to set
*/ /**
public void setAlgorithm(String algorithm) { * @param algorithm the algorithm to set
this.algorithm = algorithm; */
} public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
/** }
* Ensures that the 'alg' of the given JWT matches the {@link #algorithm} of this signer
*/ /**
@Override * Ensures that the 'alg' of the given JWT matches the {@link #algorithm} of this signer
public void sign(Jwt jwt) { */
if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) { @Override
// algorithm type doesn't match public void sign(Jwt jwt) {
// TODO: should this be an error or should we just fix it in the incoming jwt? if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) {
// for now, we fix the Jwt // algorithm type doesn't match
jwt.getHeader().setAlgorithm(algorithm); // TODO: should this be an error or should we just fix it in the incoming jwt?
} // for now, we fix the Jwt
jwt.getHeader().setAlgorithm(algorithm);
String sig = generateSignature(jwt.getSignatureBase()); }
jwt.setSignature(sig); String sig = generateSignature(jwt.getSignatureBase());
}
jwt.setSignature(sig);
/* (non-Javadoc) }
* @see org.mitre.jwt.JwtSigner#verify(java.lang.String)
*/ /* (non-Javadoc)
@Override * @see org.mitre.jwt.JwtSigner#verify(java.lang.String)
public boolean verify(String jwtString) { */
// split on the dots @Override
List<String> parts = Lists.newArrayList(Splitter.on(".").split(jwtString)); public boolean verify(String jwtString) {
// split on the dots
if (parts.size() != 3) { List<String> parts = Lists.newArrayList(Splitter.on(".").split(jwtString));
throw new IllegalArgumentException("Invalid JWT format.");
} if (parts.size() != 3) {
throw new IllegalArgumentException("Invalid JWT format.");
String h64 = parts.get(0); }
String c64 = parts.get(1);
String s64 = parts.get(2); String h64 = parts.get(0);
String c64 = parts.get(1);
String expectedSignature = generateSignature(h64 + "." + c64 + "."); String s64 = parts.get(2);
return Strings.nullToEmpty(s64).equals(Strings.nullToEmpty(expectedSignature)); String expectedSignature = generateSignature(h64 + "." + c64 + ".");
} return Strings.nullToEmpty(s64).equals(Strings.nullToEmpty(expectedSignature));
}
protected abstract String generateSignature(String signatureBase);
protected abstract String generateSignature(String signatureBase);
} }

View File

@ -1,9 +1,11 @@
package org.mitre.jwt; package org.mitre.jwt.signer;
public interface JwtSigner { import org.mitre.jwt.model.Jwt;
public void sign(Jwt jwt); public interface JwtSigner {
public boolean verify(String jwtString); public void sign(Jwt jwt);
} public boolean verify(String jwtString);
}

View File

@ -0,0 +1,18 @@
package org.mitre.jwt.signer.impl;
import org.mitre.jwt.signer.AbstractJwtSigner;
public class Es256Signer extends AbstractJwtSigner {
public Es256Signer(String algorithm) {
super(algorithm);
// TODO Auto-generated constructor stub
}
@Override
protected String generateSignature(String signatureBase) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -1,90 +1,91 @@
package org.mitre.jwt; package org.mitre.jwt.signer.impl;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.mitre.jwt.signer.AbstractJwtSigner;
public class Hmac256Signer extends AbstractJwtSigner {
public class Hmac256Signer extends AbstractJwtSigner {
private Mac mac;
private Mac mac;
private byte[] passphrase;
private byte[] passphrase;
public Hmac256Signer() {
this(null); public Hmac256Signer() {
} this(null);
}
public Hmac256Signer(byte[] passphrase) {
super(HS256); public Hmac256Signer(byte[] passphrase) {
super(HS256);
//TODO: set up a factory for other signature methods
//TODO: set up a factory for other signature methods
setPassphrase(passphrase);
setPassphrase(passphrase);
try {
mac = Mac.getInstance("HMACSHA256"); try {
} catch (NoSuchAlgorithmException e) { mac = Mac.getInstance("HMACSHA256");
// TODO Auto-generated catch block } catch (NoSuchAlgorithmException e) {
e.printStackTrace(); // TODO Auto-generated catch block
} e.printStackTrace();
}
}
}
@Override
protected String generateSignature(String signatureBase) { @Override
if (passphrase == null) { protected String generateSignature(String signatureBase) {
return null; // TODO: probably throw some kind of exception if (passphrase == null) {
} return null; // TODO: probably throw some kind of exception
}
try {
mac.init(new SecretKeySpec(getPassphrase(), mac.getAlgorithm())); try {
} catch (InvalidKeyException e) { mac.init(new SecretKeySpec(getPassphrase(), mac.getAlgorithm()));
// TODO Auto-generated catch block } catch (InvalidKeyException e) {
e.printStackTrace(); // TODO Auto-generated catch block
} e.printStackTrace();
}
try {
mac.update(signatureBase.getBytes("UTF-8")); try {
} catch (IllegalStateException e) { mac.update(signatureBase.getBytes("UTF-8"));
// TODO Auto-generated catch block } catch (IllegalStateException e) {
e.printStackTrace(); // TODO Auto-generated catch block
} catch (UnsupportedEncodingException e) { e.printStackTrace();
// TODO Auto-generated catch block } catch (UnsupportedEncodingException e) {
e.printStackTrace(); // TODO Auto-generated catch block
} e.printStackTrace();
}
byte[] sigBytes = mac.doFinal();
byte[] sigBytes = mac.doFinal();
String sig = new String(Base64.encodeBase64URLSafe(sigBytes));
String sig = new String(Base64.encodeBase64URLSafe(sigBytes));
// strip off any padding
sig = sig.replace("=", ""); // strip off any padding
return sig; sig = sig.replace("=", "");
} return sig;
}
/**
* @return the passphrase /**
*/ * @return the passphrase
public byte[] getPassphrase() { */
return passphrase; public byte[] getPassphrase() {
} return passphrase;
}
/**
* @param passphrase the passphrase to set /**
*/ * @param passphrase the passphrase to set
public void setPassphrase(byte[] passphrase) { */
this.passphrase = passphrase; public void setPassphrase(byte[] passphrase) {
} this.passphrase = passphrase;
}
}
}

View File

@ -1,14 +1,16 @@
package org.mitre.jwt; package org.mitre.jwt.signer.impl;
public class PlaintextSigner extends AbstractJwtSigner { import org.mitre.jwt.signer.AbstractJwtSigner;
public PlaintextSigner() { public class PlaintextSigner extends AbstractJwtSigner {
super(PLAINTEXT);
} public PlaintextSigner() {
super(PLAINTEXT);
@Override }
protected String generateSignature(String signatureBase) {
return null; @Override
} protected String generateSignature(String signatureBase) {
return null;
} }
}

View File

@ -0,0 +1,85 @@
package org.mitre.jwt.signer.impl;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import org.apache.commons.codec.binary.Base64;
import org.mitre.jwt.signer.AbstractJwtSigner;
/**
* JWT Signer using RSA SHA-256 algorithm
* @author AANGANES
*
*/
public class Rs256Signer extends AbstractJwtSigner {
//TODO: should this class generate a new private key or get one passed into the constructor?
private PrivateKey privateKey;
private Signature signer;
public Rs256Signer() {
this(null);
}
public Rs256Signer(PrivateKey privateKey) {
super("RS256");
setPrivateKey(privateKey);
try {
signer = Signature.getInstance("SHA256withRSA");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected String generateSignature(String signatureBase) {
try {
signer.initSign(privateKey);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
signer.update(signatureBase.getBytes("UTF-8"));
} catch (SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] sigBytes;
String sig = "";
try {
sigBytes = signer.sign();
sig = new String(Base64.encodeBase64URLSafe(sigBytes));
// strip off any padding
sig = sig.replace("=", "");
} catch (SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sig;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
}
}

View File

@ -2,7 +2,7 @@ package org.mitre.openid.connect.model;
import javax.persistence.Entity; import javax.persistence.Entity;
import org.mitre.jwt.Jwt; import org.mitre.jwt.model.Jwt;
/* /*
* TODO: This class needs to be encoded as a JWT * TODO: This class needs to be encoded as a JWT

View File

@ -1,126 +1,131 @@
package org.mitre.jwt; package org.mitre.jwt;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Date; import java.util.Date;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.mitre.jwt.model.Jwt;
import org.mitre.jwt.signer.AbstractJwtSigner;
public class JwtTest { import org.mitre.jwt.signer.JwtSigner;
import org.mitre.jwt.signer.impl.Hmac256Signer;
@Test import org.mitre.jwt.signer.impl.PlaintextSigner;
public void testToStringPlaintext() { import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
Jwt jwt = new Jwt();
jwt.getHeader().setAlgorithm("none"); public class JwtTest {
jwt.getClaims().setIssuer("joe");
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L)); @Test
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE); public void testToStringPlaintext() {
Jwt jwt = new Jwt();
// sign it with a blank signature jwt.getHeader().setAlgorithm("none");
JwtSigner signer = new PlaintextSigner(); jwt.getClaims().setIssuer("joe");
signer.sign(jwt); jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
/*
* Expected string based on the following structures, serialized exactly as follows and base64 encoded: // sign it with a blank signature
* JwtSigner signer = new PlaintextSigner();
* header: {"alg":"none"} signer.sign(jwt);
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
*/ /*
String expected = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."; * Expected string based on the following structures, serialized exactly as follows and base64 encoded:
*
String actual = jwt.toString(); * header: {"alg":"none"}
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
assertThat(actual, equalTo(expected)); */
String expected = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
}
String actual = jwt.toString();
@Test
public void testGenerateHmacSignature() { assertThat(actual, equalTo(expected));
Jwt jwt = new Jwt();
jwt.getHeader().setType("JWT"); }
jwt.getHeader().setAlgorithm("HS256");
jwt.getClaims().setIssuer("joe"); @Test
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L)); public void testGenerateHmacSignature() {
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE); Jwt jwt = new Jwt();
jwt.getHeader().setType("JWT");
// sign it jwt.getHeader().setAlgorithm("HS256");
byte[] key = null; jwt.getClaims().setIssuer("joe");
try { jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
key = "secret".getBytes("UTF-8"); jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block // sign it
e.printStackTrace(); byte[] key = null;
} try {
key = "secret".getBytes("UTF-8");
JwtSigner signer = new Hmac256Signer(key); } catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
signer.sign(jwt); e.printStackTrace();
}
/*
* Expected string based on the following strucutres, serialized exactly as follows and base64 encoded: JwtSigner signer = new Hmac256Signer(key);
*
* header: {"typ":"JWT","alg":"HS256"} signer.sign(jwt);
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
* /*
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E * Expected string based on the following strucutres, serialized exactly as follows and base64 encoded:
* *
*/ * header: {"typ":"JWT","alg":"HS256"}
String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y"; * claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." + signature; *
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
String actual = jwt.toString(); *
*/
assertThat(actual, equalTo(expected)); String signature = "p-63Jzz7mgi3H4hvW6MFB7lmPRZjhsL666MYkmpX33Y";
assertThat(jwt.getSignature(), equalTo(signature)); String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ." + signature;
} String actual = jwt.toString();
@Test assertThat(actual, equalTo(expected));
public void testValidateHmacSignature() { assertThat(jwt.getSignature(), equalTo(signature));
// sign it
byte[] key = null; }
try {
key = "secret".getBytes("UTF-8"); @Test
} catch (UnsupportedEncodingException e) { public void testValidateHmacSignature() {
// TODO Auto-generated catch block // sign it
e.printStackTrace(); byte[] key = null;
} try {
key = "secret".getBytes("UTF-8");
JwtSigner signer = new Hmac256Signer(key); } catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
/* e.printStackTrace();
* Token string based on the following strucutres, serialized exactly as follows and base64 encoded: }
*
* header: {"typ":"JWT","alg":"HS256"} JwtSigner signer = new Hmac256Signer(key);
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
* /*
* Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E * Token string based on the following strucutres, serialized exactly as follows and base64 encoded:
* *
*/ * header: {"typ":"JWT","alg":"HS256"}
String jwtString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E"; * claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
*
boolean valid = signer.verify(jwtString); * Expected signature: iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E
*
assertThat(valid, equalTo(Boolean.TRUE)); */
String jwtString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.iGBPJj47S5q_HAhSoQqAdcS6A_1CFj3zrLaImqNbt9E";
}
boolean valid = signer.verify(jwtString);
@Test
public void testParse() { assertThat(valid, equalTo(Boolean.TRUE));
String source = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
}
Jwt jwt = Jwt.parse(source); @Test
public void testParse() {
assertThat(jwt.getHeader().getAlgorithm(), equalTo(AbstractJwtSigner.PLAINTEXT)); String source = "eyJhbGciOiJub25lIn0.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.";
assertThat(jwt.getClaims().getIssuer(), equalTo("joe"));
assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L)));
assertThat((Boolean)jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE)); Jwt jwt = Jwt.parse(source);
} assertThat(jwt.getHeader().getAlgorithm(), equalTo(AbstractJwtSigner.PLAINTEXT));
assertThat(jwt.getClaims().getIssuer(), equalTo("joe"));
} assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L)));
assertThat((Boolean)jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE));
}
}