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
parent
204df8f0bd
commit
15f8675e1a
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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 + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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 + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue