deleted local JOSE implementation classes
parent
46f0e6f3cb
commit
6dc9020a31
|
@ -1,120 +0,0 @@
|
||||||
package org.mitre.jwe.model;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.mitre.jwt.model.Jwt;
|
|
||||||
import org.mitre.jwt.model.JwtHeader;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Return the canonical encoded string of this JWE, the header in Base64, a period ".", the encrypted key in Base64, a period ".",
|
|
||||||
* the ciphertext in Base64, a period ".", and the signature, or integrity value, in Base64.
|
|
||||||
|
|
||||||
* @author DERRYBERRY
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Jwe extends Jwt {
|
|
||||||
|
|
||||||
private byte[] encryptedKey;
|
|
||||||
|
|
||||||
private byte[] ciphertext;
|
|
||||||
|
|
||||||
public Jwe() {
|
|
||||||
super();
|
|
||||||
this.encryptedKey = null;
|
|
||||||
this.ciphertext = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Jwe(JweHeader header, byte[] encryptedKey, byte[] ciphertext, String integrityValue) {
|
|
||||||
super(header, null, integrityValue);
|
|
||||||
this.encryptedKey = encryptedKey;
|
|
||||||
this.ciphertext = ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
public Jwe(String headerBase64, String encryptedKeyBase64, String cipherTextBase64, String integrityValueBase64) {
|
|
||||||
byte[] decodedEncryptedKey = Base64.decodeBase64(encryptedKeyBase64.getBytes());
|
|
||||||
byte[] decodedCipherText = Base64.decodeBase64(cipherTextBase64.getBytes());
|
|
||||||
this.header = new JweHeader(headerBase64);
|
|
||||||
this.encryptedKey = decodedEncryptedKey;
|
|
||||||
this.ciphertext = decodedCipherText;
|
|
||||||
setSignature(integrityValueBase64);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public JweHeader getHeader() {
|
|
||||||
return (JweHeader) super.getHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the header, wrapping it in a JweHeader if necessary
|
|
||||||
*/
|
|
||||||
public void setHeader(JwtHeader header) {
|
|
||||||
if (header instanceof JweHeader) {
|
|
||||||
super.setHeader(header);
|
|
||||||
} else {
|
|
||||||
super.setHeader(new JweHeader(header));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getEncryptedKey() {
|
|
||||||
return encryptedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEncryptedKey(byte[] encryptedKey) {
|
|
||||||
this.encryptedKey = encryptedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getCiphertext() {
|
|
||||||
return ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCiphertext(byte[] ciphertext) {
|
|
||||||
this.ciphertext = ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSignatureBase() {
|
|
||||||
String h64 = new String(Base64.encodeBase64URLSafe(getHeader().toJsonString().getBytes()));
|
|
||||||
String e64 = new String(Base64.encodeBase64URLSafe(getEncryptedKey()));
|
|
||||||
String c64 = new String(Base64.encodeBase64URLSafe(getCiphertext()));
|
|
||||||
|
|
||||||
return h64 + "." + e64 + "." + c64;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Jwe parse(String s) {
|
|
||||||
|
|
||||||
// null string is a null token
|
|
||||||
if (s == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// split on the dots
|
|
||||||
List<String> parts = Lists.newArrayList(Splitter.on(".").split(s));
|
|
||||||
|
|
||||||
if (parts.size() != 4) {
|
|
||||||
throw new IllegalArgumentException("Invalid JWE format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
String h64 = parts.get(0);
|
|
||||||
String e64 = parts.get(1);
|
|
||||||
String c64 = parts.get(2);
|
|
||||||
String i64 = parts.get(3);
|
|
||||||
|
|
||||||
byte[] decodedEncryptedKey = Base64.decodeBase64(e64.getBytes());
|
|
||||||
byte[] decodedCipherText = Base64.decodeBase64(c64.getBytes());
|
|
||||||
|
|
||||||
Jwe jwe = new Jwe(new JweHeader(h64), decodedEncryptedKey, decodedCipherText, i64);
|
|
||||||
|
|
||||||
return jwe;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,165 +0,0 @@
|
||||||
package org.mitre.jwe.model;
|
|
||||||
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import org.mitre.jwt.model.JwtHeader;
|
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
public class JweHeader extends JwtHeader{
|
|
||||||
|
|
||||||
public static final String INTEGRITY = "int";
|
|
||||||
public static final String INITIALIZATION_VECTOR = "iv";
|
|
||||||
public static final String EPHEMERAL_PUBLIC_KEY = "epk";
|
|
||||||
public static final String COMPRESSION_ALGORITHM = "zip";
|
|
||||||
public static final String JSON_SET_URL = "jku";
|
|
||||||
public static final String JSON_WEB_KEY = "jwk";
|
|
||||||
public static final String X509_URL = "x5u";
|
|
||||||
public static final String X509_CERTIFICATE_THUMBPRINT = "x5t";
|
|
||||||
public static final String X509_CERTIFICATE_CHAIN = "x5c";
|
|
||||||
public static final String KEY_ID = "kid";
|
|
||||||
public static final String KEY_DERIVATION_FUNCTION = "kdf";
|
|
||||||
|
|
||||||
public JweHeader(){
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JweHeader(JsonObject object){
|
|
||||||
super(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JweHeader(String b64) {
|
|
||||||
super(b64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JweHeader(JwtHeader jwtHeader) {
|
|
||||||
super(jwtHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load all claims from the given json object into this object
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void loadFromJsonObject(JsonObject json) {
|
|
||||||
|
|
||||||
JsonObject pass = new JsonObject();
|
|
||||||
|
|
||||||
for (Entry<String, JsonElement> element : json.entrySet()) {
|
|
||||||
if (element.getValue().isJsonNull()) {
|
|
||||||
pass.add(element.getKey(), element.getValue());
|
|
||||||
} else if (element.getKey().equals(INTEGRITY)) {
|
|
||||||
this.setIntegrity(json.get(INTEGRITY).getAsString());
|
|
||||||
} else if (element.getKey().equals(INITIALIZATION_VECTOR)) {
|
|
||||||
this.setIv(json.get(INITIALIZATION_VECTOR).getAsString());
|
|
||||||
} else if (element.getKey().equals(EPHEMERAL_PUBLIC_KEY)) {
|
|
||||||
this.setEphemeralPublicKey(json.get(EPHEMERAL_PUBLIC_KEY).getAsString());
|
|
||||||
} else if (element.getKey().equals(COMPRESSION_ALGORITHM)) {
|
|
||||||
this.setCompressionAlgorithm(json.get(COMPRESSION_ALGORITHM).getAsString());
|
|
||||||
} else if (element.getKey().equals(JSON_SET_URL)) {
|
|
||||||
this.setJku(json.get(JSON_SET_URL).getAsString());
|
|
||||||
} else if (element.getKey().equals(JSON_WEB_KEY)) {
|
|
||||||
this.setJsonWebKey(json.get(JSON_WEB_KEY).getAsString());
|
|
||||||
} else if (element.getKey().equals(X509_URL)) {
|
|
||||||
this.setX509Url(json.get(X509_URL).getAsString());
|
|
||||||
} else if (element.getKey().equals(X509_CERTIFICATE_THUMBPRINT)) {
|
|
||||||
this.setX509CertThumbprint(json.get(X509_CERTIFICATE_THUMBPRINT).getAsString());
|
|
||||||
} else if (element.getKey().equals(X509_CERTIFICATE_CHAIN)) {
|
|
||||||
this.setX509CertChain(json.get(X509_CERTIFICATE_CHAIN).getAsString());
|
|
||||||
} else if (element.getKey().equals(KEY_ID)) {
|
|
||||||
this.setKeyId(json.get(KEY_ID).getAsString());
|
|
||||||
} else {
|
|
||||||
pass.add(element.getKey(), element.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.loadFromJsonObject(pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIntegrity() {
|
|
||||||
return getClaimAsString(INTEGRITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getInitializationVector() {
|
|
||||||
return getClaimAsString(INITIALIZATION_VECTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEphemeralPublicKey() {
|
|
||||||
return getClaimAsString(EPHEMERAL_PUBLIC_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCompressionAlgorithm() {
|
|
||||||
return getClaimAsString(COMPRESSION_ALGORITHM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getJsonSetUrl() {
|
|
||||||
return getClaimAsString(JSON_SET_URL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getJsonWebKey() {
|
|
||||||
return getClaimAsString(JSON_WEB_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getX509Url() {
|
|
||||||
return getClaimAsString(X509_URL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getX509CertificateThumbprint() {
|
|
||||||
return getClaimAsString(X509_CERTIFICATE_THUMBPRINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getX509CertificateChain() {
|
|
||||||
return getClaimAsString(X509_CERTIFICATE_CHAIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKeyId() {
|
|
||||||
return getClaimAsString(KEY_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKeyDerivationFunction() {
|
|
||||||
return getClaimAsString(KEY_DERIVATION_FUNCTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIv(String iv) {
|
|
||||||
setClaim(INITIALIZATION_VECTOR, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setJku(String jku) {
|
|
||||||
setClaim(JSON_SET_URL, jku);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIntegrity(String integrity) {
|
|
||||||
setClaim(INTEGRITY, integrity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEphemeralPublicKey(String epk) {
|
|
||||||
setClaim(EPHEMERAL_PUBLIC_KEY, epk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCompressionAlgorithm(String zip) {
|
|
||||||
setClaim(COMPRESSION_ALGORITHM, zip);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setJsonWebKey(String jwk) {
|
|
||||||
setClaim(JSON_WEB_KEY, jwk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX509Url(String x5u) {
|
|
||||||
setClaim(X509_URL, x5u);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX509CertThumbprint(String x5t) {
|
|
||||||
setClaim(X509_CERTIFICATE_THUMBPRINT, x5t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX509CertChain(String x5c) {
|
|
||||||
setClaim(X509_CERTIFICATE_CHAIN, x5c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyId(String kid) {
|
|
||||||
setClaim(KEY_ID, kid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeyDerivationFunction(String kdf) {
|
|
||||||
setClaim(KEY_DERIVATION_FUNCTION, kdf);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package org.mitre.jwk.model;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
public abstract class AbstractJwk implements Jwk{
|
|
||||||
|
|
||||||
public static final String ALGORITHM = "alg";
|
|
||||||
public static final String USE = "use";
|
|
||||||
public static final String KEY_ID = "kid";
|
|
||||||
|
|
||||||
private String kid;
|
|
||||||
private String alg;
|
|
||||||
private String use;
|
|
||||||
|
|
||||||
public AbstractJwk(JsonObject object){
|
|
||||||
init(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.mitre.jwk.model.Jwk2#getAlg()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getAlg() {
|
|
||||||
return alg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlg(String alg) {
|
|
||||||
this.alg = alg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.mitre.jwk.model.Jwk2#getKid()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getKid() {
|
|
||||||
return kid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKid(String kid) {
|
|
||||||
this.kid = kid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.mitre.jwk.model.Jwk2#getUse()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getUse() {
|
|
||||||
return use;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUse(String use) {
|
|
||||||
this.use = use;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonObject toJsonObject() {
|
|
||||||
JsonObject export = new JsonObject();
|
|
||||||
export.addProperty(ALGORITHM, getAlg());
|
|
||||||
export.addProperty(USE, getUse());
|
|
||||||
export.addProperty(KEY_ID, getKid());
|
|
||||||
return export;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void init(JsonObject object){
|
|
||||||
if(object.get(ALGORITHM) != null){
|
|
||||||
setAlg(object.get(ALGORITHM).getAsString());
|
|
||||||
}
|
|
||||||
if(object.get(KEY_ID) != null){
|
|
||||||
setKid(object.get(KEY_ID).getAsString());
|
|
||||||
}
|
|
||||||
if(object.get(USE) != null){
|
|
||||||
setUse(object.get(USE).getAsString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
package org.mitre.jwk.model;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.spec.ECFieldF2m;
|
|
||||||
import java.security.spec.EllipticCurve;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
|
||||||
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
|
|
||||||
import org.bouncycastle.math.ec.ECCurve;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
public class EllipticCurveJwk extends AbstractJwk {
|
|
||||||
|
|
||||||
public static final String CURVE = "crv";
|
|
||||||
public static final String X = "x";
|
|
||||||
public static final String Y = "y";
|
|
||||||
|
|
||||||
private String crv;
|
|
||||||
private String x;
|
|
||||||
private String y;
|
|
||||||
|
|
||||||
JsonObject object = new JsonObject();
|
|
||||||
|
|
||||||
public String getCrv() {
|
|
||||||
return crv;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCrv(String crv) {
|
|
||||||
this.crv = crv;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX(String x) {
|
|
||||||
this.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setY(String y) {
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EllipticCurveJwk(JsonObject object) {
|
|
||||||
super(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonObject toJsonObject() {
|
|
||||||
JsonObject obj = super.toJsonObject();
|
|
||||||
obj.addProperty(CURVE, getCrv());
|
|
||||||
obj.addProperty(X, getX());
|
|
||||||
obj.addProperty(Y, getY());
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(JsonObject object){
|
|
||||||
super.init(object);
|
|
||||||
setCrv(object.get(CURVE).getAsString());
|
|
||||||
setX(object.get(X).getAsString());
|
|
||||||
setY(object.get(Y).getAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PublicKey getKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
byte[] x_byte = Base64.decodeBase64(x);
|
|
||||||
BigInteger x_int = new BigInteger(x_byte);
|
|
||||||
byte[] y_byte = Base64.decodeBase64(y);
|
|
||||||
BigInteger y_int = new BigInteger(y_byte);
|
|
||||||
|
|
||||||
ECNamedCurveParameterSpec curveSpec = ECNamedCurveTable.getParameterSpec(crv);
|
|
||||||
BigInteger orderOfGen = curveSpec.getH();
|
|
||||||
int cofactor = Math.abs(curveSpec.getN().intValue());
|
|
||||||
ECCurve crv = curveSpec.getCurve();
|
|
||||||
BigInteger a = crv.getA().toBigInteger();
|
|
||||||
BigInteger b = crv.getB().toBigInteger();
|
|
||||||
int fieldSize = crv.getFieldSize();
|
|
||||||
ECFieldF2m field = new ECFieldF2m(fieldSize);
|
|
||||||
EllipticCurve curve = new EllipticCurve(field, a, b);
|
|
||||||
//ECPoint.Fp point = new ECPoint.Fp(curve, arg1, arg2);
|
|
||||||
return null;
|
|
||||||
|
|
||||||
//ECParameterSpec paramSpec = new ECParameterSpec(curve, point, orderOfGen, cofactor);
|
|
||||||
//ECPublicKeySpec spec = new ECPublicKeySpec(point, paramSpec);
|
|
||||||
//PublicKey key = new JCEECPublicKey("ECDCA", spec);
|
|
||||||
|
|
||||||
//return key;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package org.mitre.jwk.model;
|
|
||||||
|
|
||||||
import java.security.Key;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
|
|
||||||
public interface Jwk {
|
|
||||||
|
|
||||||
public abstract String getAlg();
|
|
||||||
|
|
||||||
public abstract String getKid();
|
|
||||||
|
|
||||||
public abstract String getUse();
|
|
||||||
|
|
||||||
public abstract Key getKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package org.mitre.jwk.model;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.security.spec.RSAPublicKeySpec;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
public class Rsa extends AbstractJwk{
|
|
||||||
|
|
||||||
public static final String MODULUS = "mod";
|
|
||||||
public static final String EXPONENT = "exp";
|
|
||||||
|
|
||||||
private String mod;
|
|
||||||
private String exp;
|
|
||||||
|
|
||||||
public String getMod() {
|
|
||||||
return mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMod(String mod) {
|
|
||||||
this.mod = mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExp() {
|
|
||||||
return exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExp(String exp) {
|
|
||||||
this.exp = exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rsa(JsonObject object){
|
|
||||||
super(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(JsonObject object){
|
|
||||||
super.init(object);
|
|
||||||
setMod(object.get(MODULUS).getAsString());
|
|
||||||
setExp(object.get(EXPONENT).getAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonObject toJsonObject() {
|
|
||||||
JsonObject export = super.toJsonObject();
|
|
||||||
export.addProperty(MODULUS, getMod());
|
|
||||||
export.addProperty(EXPONENT, getExp());
|
|
||||||
return export;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PublicKey getKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
||||||
byte[] modulusByte = Base64.decodeBase64(mod);
|
|
||||||
BigInteger modulus = new BigInteger(modulusByte);
|
|
||||||
byte[] exponentByte = Base64.decodeBase64(exp);
|
|
||||||
BigInteger exponent = new BigInteger(exponentByte);
|
|
||||||
|
|
||||||
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
|
|
||||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
|
||||||
PublicKey pub = factory.generatePublic(spec);
|
|
||||||
|
|
||||||
return pub;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.mitre.jwt.encryption;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import com.google.common.primitives.Ints;
|
|
||||||
|
|
||||||
public abstract class AbstractJweDecrypter implements JweDecrypter {
|
|
||||||
|
|
||||||
long MAX_HASH_INPUTLEN = Long.MAX_VALUE;
|
|
||||||
long UNSIGNED_INT_MAX_VALUE = 4294967395L;
|
|
||||||
|
|
||||||
public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) throws NoSuchAlgorithmException {
|
|
||||||
|
|
||||||
MessageDigest md = null;
|
|
||||||
//HUGE DISCLAIMER: this won't work on windows machines that don't have jce unlimited security files installed.
|
|
||||||
//without it, keys can't be over 128 bit in length, and SHA-128 doesn't work for message digest.
|
|
||||||
|
|
||||||
//use keyDataLen to determine instance
|
|
||||||
md = MessageDigest.getInstance("SHA-" + Integer.toString(keyDataLen));
|
|
||||||
|
|
||||||
keyDataLen = keyDataLen / 8;
|
|
||||||
byte[] key = new byte[keyDataLen];
|
|
||||||
int hashLen = md.getDigestLength();
|
|
||||||
int reps = keyDataLen / hashLen;
|
|
||||||
if (reps > UNSIGNED_INT_MAX_VALUE) {
|
|
||||||
throw new IllegalArgumentException("Key derivation failed");
|
|
||||||
}
|
|
||||||
int counter = 1;
|
|
||||||
byte[] counterInBytes = Ints.toByteArray(counter);
|
|
||||||
if ((counterInBytes.length + cmk.length + type.length) * 8 > MAX_HASH_INPUTLEN) {
|
|
||||||
throw new IllegalArgumentException("Key derivation failed");
|
|
||||||
}
|
|
||||||
for (int i = 0; i <= reps; i++) {
|
|
||||||
md.reset();
|
|
||||||
md.update(Ints.toByteArray(i + 1));
|
|
||||||
md.update(cmk);
|
|
||||||
md.update(type);
|
|
||||||
byte[] hash = md.digest();
|
|
||||||
if (i < reps) {
|
|
||||||
System.arraycopy(hash, 0, key, hashLen * i, hashLen);
|
|
||||||
} else {
|
|
||||||
System.arraycopy(hash, 0, key, hashLen * i, keyDataLen % hashLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.mitre.jwt.encryption;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import com.google.common.primitives.Ints;
|
|
||||||
|
|
||||||
public abstract class AbstractJweEncrypter implements JweEncrypter {
|
|
||||||
|
|
||||||
public MessageDigest md;
|
|
||||||
|
|
||||||
public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) throws NoSuchAlgorithmException {
|
|
||||||
//HUGE DISCLAIMER: this won't work on windows machines that don't have jce unlimited security files installed.
|
|
||||||
//without it, keys can't be over 128 bit in length, and SHA-128 doesn't work for message digest.
|
|
||||||
|
|
||||||
//Use keyDataLen to determine instance
|
|
||||||
md = MessageDigest.getInstance("SHA-" + Integer.toString(keyDataLen));
|
|
||||||
|
|
||||||
long MAX_HASH_INPUTLEN = Long.MAX_VALUE;
|
|
||||||
long UNSIGNED_INT_MAX_VALUE = 4294967395L;
|
|
||||||
|
|
||||||
keyDataLen = keyDataLen / 8;
|
|
||||||
byte[] key = new byte[keyDataLen];
|
|
||||||
int hashLen = md.getDigestLength();
|
|
||||||
int reps = keyDataLen / hashLen;
|
|
||||||
if (reps > UNSIGNED_INT_MAX_VALUE) {
|
|
||||||
throw new IllegalArgumentException("Key derivation failed");
|
|
||||||
}
|
|
||||||
int counter = 1;
|
|
||||||
byte[] counterInBytes = Ints.toByteArray(counter);
|
|
||||||
if ((counterInBytes.length + cmk.length + type.length) * 8 > MAX_HASH_INPUTLEN) {
|
|
||||||
throw new IllegalArgumentException("Key derivation failed");
|
|
||||||
}
|
|
||||||
for (int i = 0; i <= reps; i++) {
|
|
||||||
md.reset();
|
|
||||||
md.update(Ints.toByteArray(i + 1));
|
|
||||||
md.update(cmk);
|
|
||||||
md.update(type);
|
|
||||||
byte[] hash = md.digest();
|
|
||||||
if (i < reps) {
|
|
||||||
System.arraycopy(hash, 0, key, hashLen * i, hashLen);
|
|
||||||
} else {
|
|
||||||
System.arraycopy(hash, 0, key, hashLen * i, keyDataLen % hashLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package org.mitre.jwt.encryption;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
|
|
||||||
public enum JweAlgorithms {
|
|
||||||
|
|
||||||
//Key Derivation Function Values
|
|
||||||
CS256("256"),
|
|
||||||
CS384("384"),
|
|
||||||
CS512("512"),
|
|
||||||
//Encryption Method Values
|
|
||||||
A128GCM("GCM"),
|
|
||||||
A256GCM("GCM"),
|
|
||||||
A128CBC("CBC"),
|
|
||||||
A256CBC("CBC");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
JweAlgorithms(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getByName(String name) {
|
|
||||||
for (JweAlgorithms correspondingType : JweAlgorithms.values()) {
|
|
||||||
if (correspondingType.toString().equals(name)) {
|
|
||||||
return correspondingType.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"JweAlgorithm name " + name + " does not have a corresponding JweAlgorithm: expected one of [" + StringUtils.join(JweAlgorithms.values(), ", ") + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package org.mitre.jwt.encryption;
|
|
||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
|
||||||
|
|
||||||
public interface JweDecrypter {
|
|
||||||
|
|
||||||
public Jwe decrypt(String encryptedJwe) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException;
|
|
||||||
|
|
||||||
public byte[] decryptCipherText(Jwe jwe, byte[] cek) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException;
|
|
||||||
|
|
||||||
public byte[] decryptEncryptionKey(Jwe jwe) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package org.mitre.jwt.encryption;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
|
||||||
|
|
||||||
import com.google.gson.JsonIOException;
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
|
||||||
|
|
||||||
|
|
||||||
public interface JweEncrypter {
|
|
||||||
|
|
||||||
public byte[] encryptKey(Jwe jwe, byte[] cmk) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException;
|
|
||||||
|
|
||||||
public byte[] encryptClaims(Jwe jwe, byte[] cik) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException;
|
|
||||||
|
|
||||||
public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException, JsonIOException, JsonSyntaxException, IOException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException;
|
|
||||||
|
|
||||||
public byte[] generateContentKey(byte[] cmk, int keyDataLen, byte[] type) throws NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
package org.mitre.jwt.encryption.impl;
|
|
||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
|
||||||
import org.mitre.jwt.encryption.AbstractJweDecrypter;
|
|
||||||
import org.mitre.jwt.encryption.JweAlgorithms;
|
|
||||||
|
|
||||||
|
|
||||||
public class RsaDecrypter extends AbstractJweDecrypter {
|
|
||||||
|
|
||||||
private PublicKey publicKey;
|
|
||||||
private PrivateKey privateKey;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Jwe decrypt(String encryptedJwe) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
|
|
||||||
|
|
||||||
Jwe jwe = Jwe.parse(encryptedJwe);
|
|
||||||
|
|
||||||
String alg = jwe.getHeader().getAlgorithm();
|
|
||||||
if(alg.equals("RSA1_5") || alg.equals("RSA-OAEP")) {
|
|
||||||
|
|
||||||
//decrypt to get cmk to be used for cek and cik
|
|
||||||
jwe.setEncryptedKey(decryptEncryptionKey(jwe));
|
|
||||||
|
|
||||||
//generation of cek and cik
|
|
||||||
byte[] contentEncryptionKey = null;
|
|
||||||
//check what the key length is
|
|
||||||
String kdf = jwe.getHeader().getKeyDerivationFunction();
|
|
||||||
String keyLength = JweAlgorithms.getByName(kdf);
|
|
||||||
int keyBitLength = Integer.parseInt(keyLength);
|
|
||||||
//generate cek and cik
|
|
||||||
contentEncryptionKey = generateContentKey(jwe.getEncryptedKey(), keyBitLength, "Encryption".getBytes());
|
|
||||||
|
|
||||||
//decrypt ciphertext to get claims
|
|
||||||
jwe.setCiphertext(decryptCipherText(jwe, contentEncryptionKey));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(jwe.getHeader().getEncryptionMethod() + " is not a valid decrypting algorithm");
|
|
||||||
}
|
|
||||||
return jwe;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] decryptCipherText(Jwe jwe, byte[] contentEncryptionKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
|
|
||||||
|
|
||||||
byte[] iv = new byte[16];
|
|
||||||
iv = Base64.decodeBase64(jwe.getHeader().getInitializationVector());
|
|
||||||
|
|
||||||
String encMethod = jwe.getHeader().getEncryptionMethod();
|
|
||||||
//TODO: should also check for A128GCM and A256GCM, but Cipher.getInstance() does not support the GCM mode. For now, don't use them
|
|
||||||
if(encMethod.equals("A128CBC") || encMethod.equals("A256CBC")) {
|
|
||||||
|
|
||||||
String mode = JweAlgorithms.getByName(encMethod);
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/" + mode + "/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(contentEncryptionKey, "AES"), new IvParameterSpec(iv));
|
|
||||||
byte[] clearText = cipher.doFinal(jwe.getCiphertext());
|
|
||||||
|
|
||||||
return clearText;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(jwe.getHeader().getEncryptionMethod() + " is not an implemented encryption method");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] decryptEncryptionKey(Jwe jwe) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
||||||
|
|
||||||
if(jwe.getHeader().getAlgorithm().equals("RSA1_5")){
|
|
||||||
Cipher cipher = Cipher.getInstance("RSA");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
|
|
||||||
byte[] contentMasterKey = cipher.doFinal(jwe.getEncryptedKey());
|
|
||||||
|
|
||||||
return contentMasterKey;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(jwe.getHeader().getAlgorithm() + " is not an implemented algorithm");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublicKey getPublicKey() {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPublicKey(PublicKey publicKey) {
|
|
||||||
this.publicKey = publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrivateKey getPrivateKey() {
|
|
||||||
return privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrivateKey(PrivateKey privateKey) {
|
|
||||||
this.privateKey = privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
package org.mitre.jwt.encryption.impl;
|
|
||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.mitre.jwe.model.Jwe;
|
|
||||||
import org.mitre.jwt.encryption.AbstractJweEncrypter;
|
|
||||||
import org.mitre.jwt.encryption.JweAlgorithms;
|
|
||||||
import org.mitre.jwt.signer.impl.HmacSigner;
|
|
||||||
|
|
||||||
public class RsaEncrypter extends AbstractJweEncrypter {
|
|
||||||
|
|
||||||
private PublicKey publicKey;
|
|
||||||
private PrivateKey privateKey;
|
|
||||||
|
|
||||||
public Jwe encryptAndSign(Jwe jwe) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException {
|
|
||||||
|
|
||||||
String alg = jwe.getHeader().getAlgorithm();
|
|
||||||
String integrityAlg = jwe.getHeader().getIntegrity();
|
|
||||||
|
|
||||||
if(alg.equals("RSA1_5") || alg.equals("RSA-OAEP")) {
|
|
||||||
|
|
||||||
//generate random content master key
|
|
||||||
|
|
||||||
//check what the key length is
|
|
||||||
String kdf = jwe.getHeader().getKeyDerivationFunction();
|
|
||||||
String keyLength = JweAlgorithms.getByName(kdf);
|
|
||||||
int keyBitLength = Integer.parseInt(keyLength);
|
|
||||||
|
|
||||||
byte[] contentMasterKey = new byte[keyBitLength];
|
|
||||||
new Random().nextBytes(contentMasterKey);
|
|
||||||
|
|
||||||
byte[] contentEncryptionKey = null;
|
|
||||||
byte[] contentIntegrityKey = null;
|
|
||||||
|
|
||||||
//generate cek and cik
|
|
||||||
contentEncryptionKey = generateContentKey(contentMasterKey, keyBitLength, "Encryption".getBytes());
|
|
||||||
contentIntegrityKey = generateContentKey(contentMasterKey, keyBitLength, "Integrity".getBytes());
|
|
||||||
|
|
||||||
//encrypt claims and cmk to get ciphertext and encrypted key
|
|
||||||
jwe.setCiphertext(encryptClaims(jwe, contentEncryptionKey));
|
|
||||||
jwe.setEncryptedKey(encryptKey(jwe, contentMasterKey));
|
|
||||||
|
|
||||||
//Signer must be hmac
|
|
||||||
if(integrityAlg.equals("HS256") || integrityAlg.equals("HS384") || integrityAlg.equals("HS512")){
|
|
||||||
|
|
||||||
HmacSigner hmacSigner = new HmacSigner(contentIntegrityKey);
|
|
||||||
jwe = (Jwe) hmacSigner.sign(jwe);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(integrityAlg + " is not a valid integrity value algorithm for signing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(alg + " is not a valid encrypting algorithm.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return jwe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] encryptKey(Jwe jwe, byte[] contentMasterKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
||||||
|
|
||||||
if(jwe.getHeader().getAlgorithm().equals("RSA1_5")){
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("RSA");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
|
|
||||||
byte[] encryptedKey = cipher.doFinal(contentMasterKey);
|
|
||||||
return encryptedKey;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(jwe.getHeader().getAlgorithm() + " is not a supported algorithm");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] encryptClaims(Jwe jwe, byte[] contentEncryptionKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeySpecException {
|
|
||||||
|
|
||||||
byte[] iv = new byte[16];
|
|
||||||
//look for iv value in header, if not there make one
|
|
||||||
if(jwe.getHeader().getInitializationVector() != null){
|
|
||||||
iv = Base64.decodeBase64(jwe.getHeader().getInitializationVector());
|
|
||||||
} else {
|
|
||||||
new Random().nextBytes(iv);
|
|
||||||
jwe.getHeader().setIv(Base64.encodeBase64String(iv));
|
|
||||||
}
|
|
||||||
|
|
||||||
String encMethod = jwe.getHeader().getEncryptionMethod();
|
|
||||||
//TODO: should also check for A128GCM and A256GCM, but Cipher.getInstance() does not support the GCM mode. For now, don't use them
|
|
||||||
if(encMethod.equals("A128CBC") || encMethod.equals("A256CBC")) {
|
|
||||||
|
|
||||||
String mode = JweAlgorithms.getByName(encMethod);
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/" + mode + "/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(contentEncryptionKey, "AES"), new IvParameterSpec(iv));
|
|
||||||
byte[] cipherText = cipher.doFinal(jwe.getCiphertext());
|
|
||||||
return cipherText;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(jwe.getHeader().getEncryptionMethod() + " is not a supported encryption method");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublicKey getPublicKey() {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPublicKey(PublicKey publicKey) {
|
|
||||||
this.publicKey = publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrivateKey getPrivateKey() {
|
|
||||||
return privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrivateKey(PrivateKey privateKey) {
|
|
||||||
this.privateKey = privateKey;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,266 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.model;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.google.gson.JsonPrimitive;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic container for JSON-based claims. Backed with a {@link Map} that preserves
|
|
||||||
* insertion order. Several convenience methods for getting and setting claims in different
|
|
||||||
* formats.
|
|
||||||
* @author jricher
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ClaimSet {
|
|
||||||
|
|
||||||
private String jsonString;
|
|
||||||
|
|
||||||
// the LinkedHashMap preserves insertion order
|
|
||||||
private Map<String, Object> claims = new LinkedHashMap<String, Object>();
|
|
||||||
|
|
||||||
public ClaimSet() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClaimSet(JsonObject json) {
|
|
||||||
loadFromJsonObject(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClaimSet(String b64) {
|
|
||||||
loadFromBase64JsonObjectString(b64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClaimSet(ClaimSet claimSet) {
|
|
||||||
loadFromClaimSet(claimSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an extension claim
|
|
||||||
*/
|
|
||||||
public Object getClaim(String key) {
|
|
||||||
return claims.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a claim as a string
|
|
||||||
*/
|
|
||||||
public String getClaimAsString(String key) {
|
|
||||||
Object v = claims.get(key);
|
|
||||||
if (v != null) {
|
|
||||||
return v.toString();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a claim as a Date
|
|
||||||
*/
|
|
||||||
public Date getClaimAsDate(String key) {
|
|
||||||
Object v = claims.get(key);
|
|
||||||
if (v != null) {
|
|
||||||
if (v instanceof Date) {
|
|
||||||
return (Date) v;
|
|
||||||
} else if (v instanceof Long) {
|
|
||||||
return new Date((Long) v);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: not convinced I like this construct
|
|
||||||
public List getClaimAsList(String key) {
|
|
||||||
Object v = claims.get(key);
|
|
||||||
if (v != null) {
|
|
||||||
if (v instanceof List<?>) {
|
|
||||||
return (List) v;
|
|
||||||
} else {
|
|
||||||
// return a list of the singular element
|
|
||||||
return Lists.newArrayList(v);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an extension claim
|
|
||||||
*/
|
|
||||||
public void setClaim(String key, Object value) {
|
|
||||||
invalidateString();
|
|
||||||
claims.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a primitive claim
|
|
||||||
*/
|
|
||||||
public void setClaim(String key, JsonPrimitive prim) {
|
|
||||||
invalidateString();
|
|
||||||
if (prim == null) {
|
|
||||||
// in case we get here with a primitive null
|
|
||||||
claims.put(key, prim);
|
|
||||||
} else if (prim.isBoolean()) {
|
|
||||||
claims.put(key, prim.getAsBoolean());
|
|
||||||
} else if (prim.isNumber()) {
|
|
||||||
claims.put(key, prim.getAsNumber());
|
|
||||||
} else if (prim.isString()) {
|
|
||||||
claims.put(key, prim.getAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invalidateString() {
|
|
||||||
jsonString = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an extension claim
|
|
||||||
*/
|
|
||||||
public Object removeClaim(String key) {
|
|
||||||
invalidateString();
|
|
||||||
return claims.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all claims from this ClaimSet
|
|
||||||
* @see java.util.Map#clear()
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
invalidateString();
|
|
||||||
claims.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a copy of this claim set as a JsonObject. The JsonObject is not
|
|
||||||
* backed by a live copy of this ClaimSet.
|
|
||||||
* @return a copy of the data in this header in a JsonObject
|
|
||||||
*/
|
|
||||||
public JsonObject getAsJsonObject() {
|
|
||||||
|
|
||||||
Gson g = new Gson();
|
|
||||||
|
|
||||||
JsonObject o = new JsonObject();
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We step through the claims object and serialize the internal values as
|
|
||||||
* appropriate to JsonElements.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (this.claims != null) {
|
|
||||||
for (Map.Entry<String, Object> claim : this.claims.entrySet()) {
|
|
||||||
if (claim.getValue() instanceof JsonElement) {
|
|
||||||
// raw JSON elements get passed through directly
|
|
||||||
o.add(claim.getKey(), (JsonElement)claim.getValue());
|
|
||||||
} else if (claim.getValue() instanceof String) {
|
|
||||||
o.addProperty(claim.getKey(), (String)claim.getValue());
|
|
||||||
} else if (claim.getValue() instanceof Number) {
|
|
||||||
o.addProperty(claim.getKey(), (Number)claim.getValue());
|
|
||||||
} else if (claim.getValue() instanceof Boolean) {
|
|
||||||
o.addProperty(claim.getKey(), (Boolean)claim.getValue());
|
|
||||||
} else if (claim.getValue() instanceof Character) {
|
|
||||||
o.addProperty(claim.getKey(), (Character)claim.getValue());
|
|
||||||
} else if (claim.getValue() instanceof Date) {
|
|
||||||
// dates get serialized out as integers
|
|
||||||
o.addProperty(claim.getKey(), ((Date)claim.getValue()).getTime() / 1000L);
|
|
||||||
} else if (claim.getValue() instanceof List) {
|
|
||||||
o.add(claim.getKey(), g.toJsonTree(claim.getValue(), new TypeToken<List<String>>(){}.getType()));
|
|
||||||
} else if (claim.getValue() != null) {
|
|
||||||
// try to put it in as a string
|
|
||||||
o.addProperty(claim.getKey(), g.toJson(claim.getValue()));
|
|
||||||
} else {
|
|
||||||
// otherwise add in as a null
|
|
||||||
o.add(claim.getKey(), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load new claims from the given json object. Will replace any existing claims, but does not clear claim set.
|
|
||||||
*
|
|
||||||
* This function is intended to be overridden by subclasses for more exact data type and claim handling.
|
|
||||||
*
|
|
||||||
* @param json
|
|
||||||
*/
|
|
||||||
public void loadFromJsonObject(JsonObject json) {
|
|
||||||
for (Entry<String, JsonElement> element : json.entrySet()) {
|
|
||||||
if (element.getValue().isJsonNull()) {
|
|
||||||
// nulls get stored as java nulls
|
|
||||||
setClaim(element.getKey(), null);
|
|
||||||
} else if (element.getValue().isJsonPrimitive()){
|
|
||||||
// we handle all primitives in here
|
|
||||||
JsonPrimitive prim = element.getValue().getAsJsonPrimitive();
|
|
||||||
setClaim(element.getKey(), prim);
|
|
||||||
} else {
|
|
||||||
setClaim(element.getKey(), element.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a new claims set from a Base64 encoded JSON Object string and caches the string used
|
|
||||||
*/
|
|
||||||
public void loadFromBase64JsonObjectString(String b64) {
|
|
||||||
byte[] b64decoded = Base64.decodeBase64(b64);
|
|
||||||
|
|
||||||
JsonParser parser = new JsonParser();
|
|
||||||
JsonObject json = parser.parse(new InputStreamReader(new ByteArrayInputStream(b64decoded))).getAsJsonObject();
|
|
||||||
|
|
||||||
loadFromJsonObject(json);
|
|
||||||
|
|
||||||
// save the string we were passed in (decoded from base64)
|
|
||||||
jsonString = new String(b64decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFromClaimSet(ClaimSet claimSet) {
|
|
||||||
|
|
||||||
loadFromJsonObject(claimSet.getAsJsonObject()); // we push to a JSON object and back to let subclasses override this
|
|
||||||
|
|
||||||
jsonString = claimSet.toJsonString(); // preserve the string on input
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toJsonString() {
|
|
||||||
if(jsonString == null) {
|
|
||||||
jsonString = this.getAsJsonObject().toString();
|
|
||||||
}
|
|
||||||
return jsonString;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.model;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
public class Jwt {
|
|
||||||
|
|
||||||
private JwtHeader header;
|
|
||||||
|
|
||||||
private JwtClaims claims;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base64Url encoded signature string
|
|
||||||
*/
|
|
||||||
private String signature;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Jwt() {
|
|
||||||
setHeader(new JwtHeader());
|
|
||||||
setClaims(new JwtClaims());
|
|
||||||
setSignature(null); // unsigned by default
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Jwt from existing components
|
|
||||||
* @param header
|
|
||||||
* @param claims
|
|
||||||
* @param signature
|
|
||||||
*/
|
|
||||||
public Jwt(JwtHeader header, JwtClaims claims, String signature) {
|
|
||||||
setHeader(header);
|
|
||||||
setClaims(claims);
|
|
||||||
setSignature(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the header
|
|
||||||
*/
|
|
||||||
public JwtHeader getHeader() {
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param header the header to set
|
|
||||||
*/
|
|
||||||
public void setHeader(JwtHeader header) {
|
|
||||||
this.header = header;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the claims
|
|
||||||
*/
|
|
||||||
public JwtClaims getClaims() {
|
|
||||||
return claims;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param claims the claims to set
|
|
||||||
*/
|
|
||||||
public void setClaims(JwtClaims claims) {
|
|
||||||
this.claims = claims;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the signature
|
|
||||||
*/
|
|
||||||
public String getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param signature the signature to set
|
|
||||||
*/
|
|
||||||
public void setSignature(String 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.
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
return getSignatureBase() + "." + Strings.nullToEmpty(this.signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The signature base of a JWT is the header in Base64, a period ".", and the claims in Base64.
|
|
||||||
*/
|
|
||||||
public String getSignatureBase() {
|
|
||||||
|
|
||||||
String h64 = new String(Base64.encodeBase64URLSafe(header.toJsonString().getBytes()));
|
|
||||||
String c64 = new String(Base64.encodeBase64URLSafe(claims.toJsonString().getBytes()));
|
|
||||||
|
|
||||||
return h64 + "." + c64;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a wire-encoded JWT
|
|
||||||
*/
|
|
||||||
public static Jwt parse(String s) {
|
|
||||||
|
|
||||||
// null string is a null token
|
|
||||||
if (s == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// split on the dots
|
|
||||||
List<String> parts = Lists.newArrayList(Splitter.on(".").split(s));
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// shuttle for return value
|
|
||||||
Jwt jwt = new Jwt(new JwtHeader(h64), new JwtClaims(c64), s64);
|
|
||||||
|
|
||||||
// TODO: save the wire-encoded string in the Jwt object itself?
|
|
||||||
|
|
||||||
return jwt;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,256 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.model;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
public class JwtClaims extends ClaimSet {
|
|
||||||
|
|
||||||
public static final String TYPE = "typ";
|
|
||||||
public static final String JWT_ID = "jti";
|
|
||||||
public static final String SUBJECT = "sub";
|
|
||||||
public static final String AUDIENCE = "aud";
|
|
||||||
public static final String ISSUER = "iss";
|
|
||||||
public static final String ISSUED_AT = "iat";
|
|
||||||
public static final String NOT_BEFORE = "nbf";
|
|
||||||
public static final String EXPIRATION = "exp";
|
|
||||||
public static final String NONCE = "nonce";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ISO8601 / RFC3339 Date Format
|
|
||||||
*/
|
|
||||||
//public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
|
|
||||||
|
|
||||||
public JwtClaims() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JwtClaims(JsonObject json) {
|
|
||||||
super(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JwtClaims(String b64) {
|
|
||||||
super(b64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JwtClaims(JwtClaims jwtClaims) {
|
|
||||||
super(jwtClaims);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadFromJsonObject(JsonObject json) {
|
|
||||||
JsonObject pass = new JsonObject();
|
|
||||||
|
|
||||||
for (Entry<String, JsonElement> element : json.entrySet()) {
|
|
||||||
if (element.getValue().isJsonNull()) {
|
|
||||||
pass.add(element.getKey(), element.getValue());
|
|
||||||
} else if (element.getKey().equals(EXPIRATION)) {
|
|
||||||
setExpiration(new Date(element.getValue().getAsLong() * 1000L));
|
|
||||||
} else if (element.getKey().equals(NOT_BEFORE)) {
|
|
||||||
setNotBefore(new Date(element.getValue().getAsLong() * 1000L));
|
|
||||||
} else if (element.getKey().equals(ISSUED_AT)) {
|
|
||||||
setIssuedAt(new Date(element.getValue().getAsLong() * 1000L));
|
|
||||||
} else if (element.getKey().equals(ISSUER)) {
|
|
||||||
setIssuer(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(AUDIENCE)) {
|
|
||||||
if (element.getValue().isJsonArray()) {
|
|
||||||
// it's an array of strings, set it as such
|
|
||||||
//setAudience(element.getValue().getAsJsonArray());
|
|
||||||
Type collectionType = new TypeToken<List<String>>(){}.getType();
|
|
||||||
List<String> values = new Gson().fromJson(element.getValue(), collectionType);
|
|
||||||
setAudience(values);
|
|
||||||
} else {
|
|
||||||
// it's a single value
|
|
||||||
setAudience(element.getValue().getAsString());
|
|
||||||
}
|
|
||||||
} else if (element.getKey().equals(SUBJECT)) {
|
|
||||||
setSubject(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(JWT_ID)) {
|
|
||||||
setJwtId(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(TYPE)) {
|
|
||||||
setType(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(NONCE)){
|
|
||||||
setNonce(element.getValue().getAsString());
|
|
||||||
}else {
|
|
||||||
pass.add(element.getKey(), element.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load all the generic claims into this object
|
|
||||||
super.loadFromJsonObject(pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the expiration
|
|
||||||
*/
|
|
||||||
public Date getExpiration() {
|
|
||||||
return getClaimAsDate(EXPIRATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param expiration the expiration to set
|
|
||||||
*/
|
|
||||||
public void setExpiration(Date expiration) {
|
|
||||||
setClaim(EXPIRATION, expiration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the notBefore
|
|
||||||
*/
|
|
||||||
public Date getNotBefore() {
|
|
||||||
return getClaimAsDate(NOT_BEFORE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param notBefore the notBefore to set
|
|
||||||
*/
|
|
||||||
public void setNotBefore(Date notBefore) {
|
|
||||||
setClaim(NOT_BEFORE, notBefore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the issuedAt
|
|
||||||
*/
|
|
||||||
public Date getIssuedAt() {
|
|
||||||
return getClaimAsDate(ISSUED_AT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param issuedAt the issuedAt to set
|
|
||||||
*/
|
|
||||||
public void setIssuedAt(Date issuedAt) {
|
|
||||||
setClaim(ISSUED_AT, issuedAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the issuer
|
|
||||||
*/
|
|
||||||
public String getIssuer() {
|
|
||||||
return getClaimAsString(ISSUER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param issuer the issuer to set
|
|
||||||
*/
|
|
||||||
public void setIssuer(String issuer) {
|
|
||||||
setClaim(ISSUER, issuer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the audience
|
|
||||||
*/
|
|
||||||
public List<String> getAudience() {
|
|
||||||
return (List<String>) getClaimAsList(AUDIENCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param audience the audience to set
|
|
||||||
*/
|
|
||||||
public void setAudience(String audience) {
|
|
||||||
setClaim(AUDIENCE, Lists.newArrayList(audience));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setAudience(List<String> audience) {
|
|
||||||
setClaim(AUDIENCE, audience);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the principal
|
|
||||||
*/
|
|
||||||
public String getSubject() {
|
|
||||||
return getClaimAsString(SUBJECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param principal the principal to set
|
|
||||||
*/
|
|
||||||
public void setSubject(String principal) {
|
|
||||||
setClaim(SUBJECT, principal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the jwtId
|
|
||||||
*/
|
|
||||||
public String getJwtId() {
|
|
||||||
return getClaimAsString(JWT_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param jwtId the jwtId to set
|
|
||||||
*/
|
|
||||||
public void setJwtId(String jwtId) {
|
|
||||||
setClaim(JWT_ID, jwtId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the type
|
|
||||||
*/
|
|
||||||
public String getType() {
|
|
||||||
return getClaimAsString(TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type the type to set
|
|
||||||
*/
|
|
||||||
public void setType(String type) {
|
|
||||||
setClaim(TYPE, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the nonce
|
|
||||||
*/
|
|
||||||
public String getNonce() {
|
|
||||||
return getClaimAsString(NONCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nonce the nonce to set
|
|
||||||
*/
|
|
||||||
public void setNonce(String nonce) {
|
|
||||||
setClaim(NONCE, nonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.mitre.jwt.model.ClaimSet#getAsJsonObject()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public JsonObject getAsJsonObject() {
|
|
||||||
JsonObject o = super.getAsJsonObject();
|
|
||||||
|
|
||||||
// special handling for audience claim
|
|
||||||
if (o.has(AUDIENCE) && o.get(AUDIENCE).isJsonArray()) {
|
|
||||||
JsonArray aud = o.get(AUDIENCE).getAsJsonArray();
|
|
||||||
// overwrite single-sized arrays as a string
|
|
||||||
if (aud.size() == 1) {
|
|
||||||
o.addProperty(AUDIENCE, aud.get(0).getAsString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.model;
|
|
||||||
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
public class JwtHeader extends ClaimSet {
|
|
||||||
|
|
||||||
public static final String TYPE = "typ";
|
|
||||||
public static final String ALGORITHM = "alg";
|
|
||||||
public static final String ENCRYPTION_METHOD = "enc";
|
|
||||||
public static final String CONTENT_TYPE = "cty";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make an empty header
|
|
||||||
*/
|
|
||||||
public JwtHeader() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a header from a JSON object
|
|
||||||
* @param json
|
|
||||||
*/
|
|
||||||
public JwtHeader(JsonObject json) {
|
|
||||||
super(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public JwtHeader(String b64) {
|
|
||||||
super(b64);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JwtHeader(JwtHeader jwtHeader) {
|
|
||||||
super(jwtHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load all claims from the given json object into this object
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void loadFromJsonObject(JsonObject json) {
|
|
||||||
|
|
||||||
JsonObject pass = new JsonObject();
|
|
||||||
|
|
||||||
for (Entry<String, JsonElement> element : json.entrySet()) {
|
|
||||||
if (element.getValue().isJsonNull()) {
|
|
||||||
pass.add(element.getKey(), element.getValue());
|
|
||||||
} else if (element.getKey().equals(TYPE)) {
|
|
||||||
this.setType(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(ALGORITHM)) {
|
|
||||||
this.setAlgorithm(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(ENCRYPTION_METHOD)) {
|
|
||||||
this.setEncryptionMethod(element.getValue().getAsString());
|
|
||||||
} else if (element.getKey().equals(CONTENT_TYPE)) {
|
|
||||||
this.setContentType(element.getValue().getAsString());
|
|
||||||
} else {
|
|
||||||
pass.add(element.getKey(), element.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now load all the ones we didn't handle specially
|
|
||||||
super.loadFromJsonObject(pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the type
|
|
||||||
*/
|
|
||||||
public String getType() {
|
|
||||||
return getClaimAsString(TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type the type to set
|
|
||||||
*/
|
|
||||||
public void setType(String type) {
|
|
||||||
setClaim(TYPE, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the algorithm
|
|
||||||
*/
|
|
||||||
public String getAlgorithm() {
|
|
||||||
return getClaimAsString(ALGORITHM);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param algorithm the algorithm to set
|
|
||||||
*/
|
|
||||||
public void setAlgorithm(String algorithm) {
|
|
||||||
setClaim(ALGORITHM, algorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the encryptionMethod
|
|
||||||
*/
|
|
||||||
public String getEncryptionMethod() {
|
|
||||||
return getClaimAsString(ENCRYPTION_METHOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param encryptionMethod the encryptionMethod to set
|
|
||||||
*/
|
|
||||||
public void setEncryptionMethod(String encryptionMethod) {
|
|
||||||
setClaim(ENCRYPTION_METHOD, encryptionMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getContentType() {
|
|
||||||
return CONTENT_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentType(String cty) {
|
|
||||||
setClaim(CONTENT_TYPE, cty);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.mitre.jwt.model.Jwt;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.base.Strings;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
public abstract class AbstractJwtSigner implements JwtSigner {
|
|
||||||
|
|
||||||
private JwsAlgorithm algorithm;
|
|
||||||
|
|
||||||
public AbstractJwtSigner(JwsAlgorithm algorithm) {
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the algorithm
|
|
||||||
*/
|
|
||||||
public JwsAlgorithm getAlgorithm() {
|
|
||||||
return algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param algorithm the algorithm to set
|
|
||||||
*/
|
|
||||||
public void setAlgorithm(JwsAlgorithm algorithm) {
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that the 'alg' of the given JWT matches the {@link #algorithm} of this signer
|
|
||||||
* and signs the jwt.
|
|
||||||
*
|
|
||||||
* @param jwt the jwt to sign
|
|
||||||
* @return the signed jwt
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Jwt sign(Jwt jwt) throws NoSuchAlgorithmException {
|
|
||||||
|
|
||||||
//TODO: need a seperate check for Jwe. As is, it will change the alg param to be the enc param
|
|
||||||
/*if (!Objects.equal(algorithm, jwt.getHeader().getAlgorithm())) {
|
|
||||||
// algorithm type doesn't match
|
|
||||||
// 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.getJwaName());
|
|
||||||
}*/
|
|
||||||
|
|
||||||
String sig = generateSignature(jwt.getSignatureBase());
|
|
||||||
|
|
||||||
jwt.setSignature(sig);
|
|
||||||
|
|
||||||
return jwt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.mitre.jwt.JwtSigner#verify(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean verify(String jwtString) throws NoSuchAlgorithmException {
|
|
||||||
// split on the dots
|
|
||||||
List<String> parts = Lists.newArrayList(Splitter.on(".").split(jwtString));
|
|
||||||
|
|
||||||
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 expectedSignature = generateSignature(h64 + "." + c64);
|
|
||||||
|
|
||||||
return Strings.nullToEmpty(s64).equals(Strings.nullToEmpty(expectedSignature));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract String generateSignature(String signatureBase) throws NoSuchAlgorithmException;
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum to translate between the JWS defined algorithm names and the JSE algorithm names
|
|
||||||
*
|
|
||||||
* @author jricher
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum JwsAlgorithm {
|
|
||||||
|
|
||||||
// PLAINTEXT
|
|
||||||
NONE("plaintext", "none"),
|
|
||||||
|
|
||||||
// HMAC
|
|
||||||
HS256("HMACSHA256", "HS256"),
|
|
||||||
HS384("HMACSHA384", "HS384"),
|
|
||||||
HS512("HMACSHA512", "HS512"),
|
|
||||||
// RSA
|
|
||||||
RS256("SHA256withRSA", "RS256"),
|
|
||||||
RS384("SHA384withRSA", "RS384"),
|
|
||||||
RS512("SHA512withRSA", "RS512");
|
|
||||||
|
|
||||||
|
|
||||||
private static final Map<String, JwsAlgorithm> jwaLookup = new HashMap<String, JwsAlgorithm>();
|
|
||||||
private static final Map<String, JwsAlgorithm> jceLookup = new HashMap<String, JwsAlgorithm>();
|
|
||||||
static {
|
|
||||||
for (JwsAlgorithm alg : JwsAlgorithm.values()) {
|
|
||||||
jwaLookup.put(alg.getJwaName(), alg);
|
|
||||||
jceLookup.put(alg.getStandardName(), alg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Algorithm for the JWS-registered name
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static JwsAlgorithm getByJwaName(String name) {
|
|
||||||
return jwaLookup.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JwsAlgorithm getByStandardName(String name) {
|
|
||||||
return jceLookup.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String standardName;
|
|
||||||
private final String jwaName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor of JwsAlgorithm
|
|
||||||
*
|
|
||||||
* @param standardName
|
|
||||||
*/
|
|
||||||
JwsAlgorithm(String standardName, String jwaName) {
|
|
||||||
this.standardName = standardName;
|
|
||||||
this.jwaName = jwaName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the Java standard JwsAlgorithm name
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getStandardName() {
|
|
||||||
return standardName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the JWA Standard name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getJwaName() {
|
|
||||||
return jwaName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.mitre.jwt.model.Jwt;
|
|
||||||
|
|
||||||
public interface JwtSigner {
|
|
||||||
|
|
||||||
public JwsAlgorithm getAlgorithm();
|
|
||||||
|
|
||||||
public Jwt sign(Jwt jwt) throws NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public boolean verify(String jwtString) throws NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,192 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer.impl;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
|
||||||
import org.mitre.jwt.signer.JwsAlgorithm;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JWT Signer using either the HMAC SHA-256, SHA-384, SHA-512 hash algorithm
|
|
||||||
*
|
|
||||||
* @author AANGANES, nemonik
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class HmacSigner extends AbstractJwtSigner implements InitializingBean {
|
|
||||||
|
|
||||||
public static final String DEFAULT_PASSPHRASE = "changeit";
|
|
||||||
|
|
||||||
public static final JwsAlgorithm DEFAULT_ALGORITHM = JwsAlgorithm.HS256;
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(HmacSigner.class);
|
|
||||||
|
|
||||||
private Mac mac;
|
|
||||||
|
|
||||||
private String passphrase = DEFAULT_PASSPHRASE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*/
|
|
||||||
public HmacSigner() {
|
|
||||||
super(DEFAULT_ALGORITHM);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create HMAC singer with default algorithm and passphrase as raw bytes
|
|
||||||
*
|
|
||||||
* @param passphraseAsRawBytes
|
|
||||||
* The passphrase as raw bytes
|
|
||||||
*/
|
|
||||||
public HmacSigner(byte[] passphraseAsRawBytes)
|
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
this(DEFAULT_ALGORITHM.getJwaName(), new String(passphraseAsRawBytes,
|
|
||||||
Charset.forName("UTF-8")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create HMAC singer with default algorithm and passphrase
|
|
||||||
*
|
|
||||||
* @param passwordAsRawBytes
|
|
||||||
* The passphrase as raw bytes
|
|
||||||
*/
|
|
||||||
public HmacSigner(String passphrase) throws NoSuchAlgorithmException {
|
|
||||||
this(DEFAULT_ALGORITHM.getJwaName(), passphrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create HMAC singer with given algorithm and password as raw bytes
|
|
||||||
*
|
|
||||||
* @param algorithmName
|
|
||||||
* The Java standard name of the requested MAC algorithm
|
|
||||||
* @param passphraseAsRawBytes
|
|
||||||
* The passphrase as raw bytes
|
|
||||||
*/
|
|
||||||
public HmacSigner(String algorithmName, byte[] passphraseAsRawBytes)
|
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
this(algorithmName, new String(passphraseAsRawBytes,
|
|
||||||
Charset.forName("UTF-8")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create HMAC singer with given algorithm and passwords
|
|
||||||
*
|
|
||||||
* @param algorithmName
|
|
||||||
* The Java standard name of the requested MAC algorithm
|
|
||||||
* @param passphrase
|
|
||||||
* the passphrase
|
|
||||||
*/
|
|
||||||
public HmacSigner(String algorithmName, String passphrase) {
|
|
||||||
super(JwsAlgorithm.getByJwaName(algorithmName));
|
|
||||||
|
|
||||||
Assert.notNull(passphrase, "A passphrase must be supplied");
|
|
||||||
|
|
||||||
setPassphrase(passphrase);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet(){
|
|
||||||
initializeMac();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.mitre.jwt.signer.AbstractJwtSigner#generateSignature(java.lang.String
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String generateSignature(String signatureBase) throws NoSuchAlgorithmException {
|
|
||||||
|
|
||||||
initializeMac();
|
|
||||||
|
|
||||||
if (passphrase == null) {
|
|
||||||
throw new IllegalArgumentException("Passphrase cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
mac.init(new SecretKeySpec(getPassphrase().getBytes(), mac
|
|
||||||
.getAlgorithm()));
|
|
||||||
|
|
||||||
mac.update(signatureBase.getBytes("UTF-8"));
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
logger.error("GeneralSecurityException in HmacSigner.java: ", e);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
logger.error("UnsupportedEncodingException in HmacSigner.java: ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] sigBytes = mac.doFinal();
|
|
||||||
|
|
||||||
String sig = new String(Base64.encodeBase64URLSafe(sigBytes));
|
|
||||||
|
|
||||||
// strip off any padding
|
|
||||||
sig = sig.replace("=", "");
|
|
||||||
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassphrase() {
|
|
||||||
return passphrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassphrase(byte[] rawbytes) {
|
|
||||||
this.setPassphrase(new String(rawbytes, Charset.forName("UTF-8")));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassphrase(String passphrase) {
|
|
||||||
this.passphrase = passphrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeMac() {
|
|
||||||
if (mac == null) {
|
|
||||||
try {
|
|
||||||
mac = Mac.getInstance(getAlgorithm().getStandardName());
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "HmacSigner [mac=" + mac + ", passphrase=" + passphrase + "]";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer.impl;
|
|
||||||
|
|
||||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
|
||||||
import org.mitre.jwt.signer.JwsAlgorithm;
|
|
||||||
|
|
||||||
public class PlaintextSigner extends AbstractJwtSigner {
|
|
||||||
|
|
||||||
public PlaintextSigner() {
|
|
||||||
super(JwsAlgorithm.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String generateSignature(String signatureBase) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,291 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer.impl;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.mitre.jwt.encryption.impl.KeyStore;
|
|
||||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
|
||||||
import org.mitre.jwt.signer.JwsAlgorithm;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JWT Signer using either the RSA SHA-256, SHA-384, SHA-512 hash algorithm
|
|
||||||
*
|
|
||||||
* @author AANGANES, nemonik
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(RsaSigner.class);
|
|
||||||
|
|
||||||
public static final String KEYPAIR_ALGORITHM = "RSA";
|
|
||||||
public static final String DEFAULT_PASSWORD = "changeit";
|
|
||||||
|
|
||||||
public static final JwsAlgorithm DEFAULT_ALGORITHM = JwsAlgorithm.RS256;
|
|
||||||
|
|
||||||
private KeyStore keystore;
|
|
||||||
private String alias;
|
|
||||||
private String password = DEFAULT_PASSWORD;
|
|
||||||
|
|
||||||
private PrivateKey privateKey;
|
|
||||||
private PublicKey publicKey;
|
|
||||||
private Signature signer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*/
|
|
||||||
public RsaSigner() {
|
|
||||||
super(DEFAULT_ALGORITHM);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RsaSigner from an algorithm name, a Java Keystore, an alias
|
|
||||||
* for the key pair, and the default password to access. Key pairs created
|
|
||||||
* with larger bit sizes obviously create larger signatures.
|
|
||||||
*
|
|
||||||
* @param algorithmName
|
|
||||||
* The algorithm name
|
|
||||||
* @param keystore
|
|
||||||
* A Java Keystore containing the key pair
|
|
||||||
* @param alias
|
|
||||||
* The alias for the key pair
|
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
|
||||||
public RsaSigner(String algorithmName, KeyStore keystore, String alias)
|
|
||||||
throws GeneralSecurityException {
|
|
||||||
this(algorithmName, keystore, alias, DEFAULT_PASSWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RsaSigner from an algorithm name, a Java Keystore, an alias
|
|
||||||
* for the key pair, and the password to access. Key pairs created with
|
|
||||||
* larger bit sizes obviously create larger signatures.
|
|
||||||
*
|
|
||||||
* @param algorithmName
|
|
||||||
* The algorithm name
|
|
||||||
* @param keystore
|
|
||||||
* A Java Keystore containing the key pair
|
|
||||||
* @param alias
|
|
||||||
* The alias for the key pair
|
|
||||||
* @param password
|
|
||||||
* The password used to access and retrieve the key pair.
|
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
|
||||||
public RsaSigner(String algorithmName, KeyStore keystore, String alias, String password) throws GeneralSecurityException {
|
|
||||||
super(JwsAlgorithm.getByJwaName(algorithmName));
|
|
||||||
|
|
||||||
setKeystore(keystore);
|
|
||||||
setAlias(alias);
|
|
||||||
setPassword(password);
|
|
||||||
|
|
||||||
loadKeysFromKeystore();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an RsaSigner from an algorithm name, and key pair. Key pairs
|
|
||||||
* created with larger bit sizes obviously create larger signatures.
|
|
||||||
*
|
|
||||||
* @param algorithmName
|
|
||||||
* The JWA algorithm name
|
|
||||||
* @param publicKey
|
|
||||||
* The public key
|
|
||||||
* @param privateKey
|
|
||||||
* The private key
|
|
||||||
*/
|
|
||||||
public RsaSigner(String algorithmName, PublicKey publicKey, PrivateKey privateKey) {
|
|
||||||
super(JwsAlgorithm.getByJwaName(algorithmName));
|
|
||||||
|
|
||||||
this.publicKey = publicKey;
|
|
||||||
this.privateKey = privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() throws NoSuchAlgorithmException, GeneralSecurityException {
|
|
||||||
initializeSigner();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the public and private keys from the keystore, identified with the configured alias and accessed with the configured password.
|
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
|
||||||
private void loadKeysFromKeystore() {
|
|
||||||
Assert.notNull(keystore, "An keystore must be supplied");
|
|
||||||
Assert.notNull(alias, "A alias must be supplied");
|
|
||||||
Assert.notNull(password, "A password must be supplied");
|
|
||||||
|
|
||||||
KeyPair keyPair = null;
|
|
||||||
try {
|
|
||||||
keyPair = keystore.getKeyPairForAlias(alias, password);
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.notNull(keyPair, "Either alias and/or password is not correct for keystore");
|
|
||||||
|
|
||||||
publicKey = keyPair.getPublic();
|
|
||||||
privateKey = keyPair.getPrivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.mitre.jwt.signer.AbstractJwtSigner#generateSignature(java.lang.String
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String generateSignature(String signatureBase) throws NoSuchAlgorithmException {
|
|
||||||
|
|
||||||
String sig = null;
|
|
||||||
|
|
||||||
initializeSigner();
|
|
||||||
|
|
||||||
try {
|
|
||||||
signer.initSign(privateKey);
|
|
||||||
signer.update(signatureBase.getBytes("UTF-8"));
|
|
||||||
|
|
||||||
byte[] sigBytes = signer.sign();
|
|
||||||
|
|
||||||
sig = (new String(Base64.encodeBase64URLSafe(sigBytes))).replace("=", "");
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
logger.error("GeneralSecurityException in RsaSigner.java: ", e);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
logger.error("UnsupportedEncodingException in RsaSigner.java: ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAlias() {
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyStore getKeystore() {
|
|
||||||
return keystore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrivateKey getPrivateKey() {
|
|
||||||
return privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublicKey getPublicKey() {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlias(String alias) {
|
|
||||||
this.alias = alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKeystore(KeyStore keyStore) {
|
|
||||||
this.keystore = keyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrivateKey(RSAPrivateKey privateKey) {
|
|
||||||
this.privateKey = privateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeSigner() throws NoSuchAlgorithmException{
|
|
||||||
if (this.keystore != null && this.alias != null && this.password != null) {
|
|
||||||
// if it looks like we're configured with a keystore, load it here
|
|
||||||
loadKeysFromKeystore();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signer == null) {
|
|
||||||
signer = Signature.getInstance(getAlgorithm().getStandardName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "RsaSigner [keystore=" + keystore + ", alias=" + alias
|
|
||||||
+ ", password=" + password + ", privateKey=" + privateKey
|
|
||||||
+ ", publicKey=" + publicKey + ", signer=" + signer + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see org.mitre.jwt.signer.AbstractJwtSigner#verify(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean verify(String jwtString) {
|
|
||||||
|
|
||||||
boolean value = false;
|
|
||||||
|
|
||||||
// split on the dots
|
|
||||||
List<String> parts = Lists.newArrayList(Splitter.on(".").split(
|
|
||||||
jwtString));
|
|
||||||
|
|
||||||
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 signingInput = h64 + "." + c64;
|
|
||||||
|
|
||||||
try {
|
|
||||||
initializeSigner();
|
|
||||||
signer.initVerify(publicKey);
|
|
||||||
signer.update(signingInput.getBytes("UTF-8"));
|
|
||||||
value = signer.verify(Base64.decodeBase64(s64));
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
logger.error("GeneralSecurityException in RsaSigner.java: ", e);
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
logger.error("UnsupportedEncodingException in RsaSigner.java: ", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer.service;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.mitre.jwt.model.Jwt;
|
|
||||||
import org.mitre.jwt.signer.JwtSigner;
|
|
||||||
|
|
||||||
public interface JwtSigningAndValidationService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all public keys this service is configured with, indexed by key id
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Map<String, JwtSigner> getAllSigners();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the signature of the given JWT against all configured signers,
|
|
||||||
* returns true if at least one of the signers validates it.
|
|
||||||
*
|
|
||||||
* @param jwtString
|
|
||||||
* the string representation of the JWT as sent on the wire
|
|
||||||
* @return true if the signature is valid, false if not
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
|
||||||
public boolean validateSignature(String jwtString);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to sign a jwt in place for a client that hasn't registered a preferred signing algorithm.
|
|
||||||
* Use the default algorithm to sign.
|
|
||||||
*
|
|
||||||
* @param jwt the jwt to sign
|
|
||||||
* @return the signed jwt
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
|
||||||
public void signJwt(Jwt jwt) throws NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a jwt using the selected algorithm. The algorithm is selected using the String parameter values specified
|
|
||||||
* in the JWT spec, section 6. I.E., "HS256" means HMAC with SHA-256 and corresponds to our HmacSigner class.
|
|
||||||
*
|
|
||||||
* @param jwt the jwt to sign
|
|
||||||
* @param alg the name of the algorithm to use, as specified in JWS s.6
|
|
||||||
* @return the signed jwt
|
|
||||||
*/
|
|
||||||
//TODO: implement later; only need signJwt(Jwt jwt) for now
|
|
||||||
//public Jwt signJwt(Jwt jwt, String alg);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: method to sign a jwt using a specified algorithm and a key id
|
|
||||||
*/
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2012 The MITRE Corporation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
******************************************************************************/
|
|
||||||
package org.mitre.jwt.signer.service.impl;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.mitre.jwt.model.Jwt;
|
|
||||||
import org.mitre.jwt.signer.JwtSigner;
|
|
||||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
|
||||||
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
public class DefaultJwtSigningAndValidationService implements JwtSigningAndValidationService, InitializingBean {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ConfigurationPropertiesBean configBean;
|
|
||||||
|
|
||||||
// map of identifier to signer
|
|
||||||
private Map<String, ? extends JwtSigner> signers = new HashMap<String, JwtSigner>();
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(DefaultJwtSigningAndValidationService.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* default constructor
|
|
||||||
*/
|
|
||||||
public DefaultJwtSigningAndValidationService() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create DefaultJwtSigningAndValidationService
|
|
||||||
*
|
|
||||||
* @param signer
|
|
||||||
* List of JwtSigners to associate with this service
|
|
||||||
*/
|
|
||||||
public DefaultJwtSigningAndValidationService(Map<String, ? extends JwtSigner> signer) {
|
|
||||||
setSigners(signer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet(){
|
|
||||||
// used for debugging...
|
|
||||||
if (!signers.isEmpty()) {
|
|
||||||
logger.info(this.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("DefaultJwtSigningAndValidationService is open for business");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Returns a copy of the collection of signers.
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.mitre.jwt.signer.service.JwtSigningAndValidationService#getAllPublicKeys
|
|
||||||
* ()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Map<String, JwtSigner> getAllSigners() {
|
|
||||||
|
|
||||||
Map<String, JwtSigner> map = new HashMap<String, JwtSigner>();
|
|
||||||
|
|
||||||
map.putAll(signers);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the JwtSigners associated with this service
|
|
||||||
*
|
|
||||||
* @param signers
|
|
||||||
* List of JwtSigners to associate with this service
|
|
||||||
*/
|
|
||||||
public void setSigners(Map<String, ? extends JwtSigner> signers) {
|
|
||||||
this.signers = signers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "DefaultJwtSigningAndValidationService [signers=" + signers + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the configBean
|
|
||||||
*/
|
|
||||||
public ConfigurationPropertiesBean getConfigBean() {
|
|
||||||
return configBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param configBean the configBean to set
|
|
||||||
*/
|
|
||||||
public void setConfigBean(ConfigurationPropertiesBean configBean) {
|
|
||||||
this.configBean = configBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a jwt in place using the configured default signer.
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void signJwt(Jwt jwt) throws NoSuchAlgorithmException {
|
|
||||||
String signerId = configBean.getDefaultJwtSigner();
|
|
||||||
|
|
||||||
JwtSigner signer = getSigners().get(signerId);
|
|
||||||
|
|
||||||
// set the signing algorithm in the JWT
|
|
||||||
jwt.getHeader().setAlgorithm(signer.getAlgorithm().getJwaName());
|
|
||||||
|
|
||||||
signer.sign(jwt);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the JwtSigners associated with this service
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Map<String, ? extends JwtSigner> getSigners() {
|
|
||||||
return signers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validateSignature(String jwtString) {
|
|
||||||
|
|
||||||
for (JwtSigner signer : getSigners().values()) {
|
|
||||||
try {
|
|
||||||
if (signer.verify(jwtString)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
// ignore, signer didn't verify signature, try the next one
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue