wrote default signer; added schema, definition parsers, handlers, and mapping files to configure; modified the signers, and added a keystore class to support
parent
b0934eb51e
commit
bb61292ec6
|
@ -1,7 +1,5 @@
|
|||
package org.mitre.jdbc.datasource.util;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
|
@ -12,6 +10,10 @@ import java.util.Stack;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* @author Matt Franklin
|
||||
*
|
||||
|
@ -21,6 +23,9 @@ import java.util.regex.Pattern;
|
|||
*
|
||||
*/
|
||||
public class SqlFileParser {
|
||||
|
||||
private static Log logger = LogFactory.getLog(SqlFileParser.class);
|
||||
|
||||
private static final Pattern WORD_PATTERN = Pattern
|
||||
.compile("^([a-zA-Z]*)[ ;]");
|
||||
private static final String CHILD_SCRIPT_INDICATOR = "@@";
|
||||
|
@ -77,9 +82,8 @@ public class SqlFileParser {
|
|||
processFile(resourceFile, sql);
|
||||
stateStack.pop();
|
||||
|
||||
//System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
|
||||
//System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> SQL:: " + sql);
|
||||
//System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>");
|
||||
logger.debug(" SQL:: " + sql);
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package org.mitre.jwt.service.impl;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.List;
|
||||
|
||||
import org.mitre.jwt.model.Jwt;
|
||||
import org.mitre.jwt.service.JwtSigningAndValidationService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* THIS IS A STUB
|
||||
*
|
||||
* TODO: Implement
|
||||
*
|
||||
* @author AANGANES
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class DefaultJwtSigningAndValidationService implements
|
||||
JwtSigningAndValidationService {
|
||||
|
||||
@Override
|
||||
public List<PublicKey> getAllPublicKeys() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateSignature(String jwtString) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateIssuedJwt(Jwt jwt) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJwtExpired(Jwt jwt) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -10,12 +10,7 @@ import com.google.common.base.Strings;
|
|||
import com.google.common.collect.Lists;
|
||||
|
||||
public abstract class AbstractJwtSigner implements JwtSigner {
|
||||
|
||||
public static final String PLAINTEXT = "none";
|
||||
public static final String HS256 = "HS256";
|
||||
public static final String HS384 = "HS384";
|
||||
public static final String HS512 = "HS512";
|
||||
|
||||
|
||||
private String algorithm;
|
||||
|
||||
public AbstractJwtSigner(String algorithm) {
|
||||
|
@ -77,6 +72,4 @@ public abstract class AbstractJwtSigner implements JwtSigner {
|
|||
|
||||
|
||||
protected abstract String generateSignature(String signatureBase);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package org.mitre.jwt.signer.impl;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
|
||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||
import org.mitre.jwt.signer.service.impl.KeyStore;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
public class EcdsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||
|
||||
/**
|
||||
* an enum for mapping a JWS name to standard algorithm name
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public enum Algorithm {
|
||||
|
||||
//Algorithm constants
|
||||
ES256("SHA256withECDSA"),
|
||||
ES384("SHA384withECDSA"),
|
||||
ES512("SHA512withECDSA");
|
||||
|
||||
private static final String DEFAULT = Algorithm.ES256.toString();
|
||||
|
||||
/**
|
||||
* Returns the Algorithm for the name
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static Algorithm getByName(String name) {
|
||||
for (Algorithm correspondingType : Algorithm.values()) {
|
||||
if (correspondingType.toString().equals(name)) {
|
||||
return correspondingType;
|
||||
}
|
||||
}
|
||||
|
||||
// corresponding type not found
|
||||
throw new IllegalArgumentException("Algorithm name does not have a corresponding Algorithm");
|
||||
}
|
||||
|
||||
private final String standardName;
|
||||
|
||||
/**
|
||||
* Constructor of Algorithm
|
||||
*
|
||||
* @param standardName
|
||||
*/
|
||||
Algorithm(String standardName) {
|
||||
this.standardName = standardName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Java standard algorithm name
|
||||
* @return
|
||||
*/
|
||||
public String getStandardName() {
|
||||
return standardName;
|
||||
}
|
||||
};
|
||||
|
||||
private KeyStore keystore;
|
||||
private String alias;
|
||||
private String password;
|
||||
|
||||
private PrivateKey privateKey;
|
||||
private PublicKey publicKey;
|
||||
private Signature signer;
|
||||
|
||||
public EcdsaSigner() {
|
||||
this(Algorithm.DEFAULT, null, null, null);
|
||||
}
|
||||
|
||||
public EcdsaSigner(String algorithmName, KeyStore keystore, String alias, String password) {
|
||||
super(algorithmName);
|
||||
|
||||
setKeystore(keystore);
|
||||
setAlias(alias);
|
||||
setPassword(password);
|
||||
|
||||
try {
|
||||
signer = Signature.getInstance(Algorithm.getByName(algorithmName).getStandardName());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
KeyPair keyPair = keystore.getKeyPairForAlias(alias, password);
|
||||
|
||||
publicKey = keyPair.getPublic();
|
||||
privateKey = keyPair.getPrivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateSignature(String signatureBase) {
|
||||
|
||||
/*
|
||||
1) Generate a digital signature of the UTF-8 representation of the JWS Signing Input
|
||||
using ECDSA P-256 SHA-256 with the desired private key. The output will be the
|
||||
EC point (R, S), where R and S are unsigned integers.
|
||||
2) Turn R and S into byte arrays in big endian order. Each array will be 32 bytes long.
|
||||
3) Concatenate the two byte arrays in the order R and then S.
|
||||
4) Base64url encode the resulting 64 byte array.
|
||||
*/
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public KeyStore getKeystore() {
|
||||
return keystore;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String jwtString) {
|
||||
|
||||
/*
|
||||
1) Take the Encoded JWS Signature and base64url decode it into a byte array.
|
||||
If decoding fails, the signed content MUST be rejected.
|
||||
2) The output of the base64url decoding MUST be a 64 byte array.
|
||||
3) Split the 64 byte array into two 32 byte arrays. The first array will be R and
|
||||
the second S. Remember that the byte arrays are in big endian byte order;
|
||||
please check the ECDSA validator in use to see what byte order it requires.
|
||||
4) Submit the UTF-8 representation of the JWS Signing Input, R, S and the public
|
||||
key (x, y) to the ECDSA P-256 SHA-256 validator.
|
||||
5) If the validation fails, the signed content MUST be rejected.
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package org.mitre.jwt.signer.impl;
|
||||
|
||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||
|
||||
public class Es256Signer extends AbstractJwtSigner {
|
||||
|
||||
public Es256Signer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Es256Signer(String algorithm) {
|
||||
super("ES256");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateSignature(String signatureBase) {
|
||||
|
||||
/*
|
||||
1) Generate a digital signature of the UTF-8 representation of the JWS Signing Input
|
||||
using ECDSA P-256 SHA-256 with the desired private key. The output will be the
|
||||
EC point (R, S), where R and S are unsigned integers.
|
||||
2) Turn R and S into byte arrays in big endian order. Each array will be 32 bytes long.
|
||||
3) Concatenate the two byte arrays in the order R and then S.
|
||||
4) Base64url encode the resulting 64 byte array.
|
||||
*/
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String jwtString) {
|
||||
|
||||
/*
|
||||
1) Take the Encoded JWS Signature and base64url decode it into a byte array.
|
||||
If decoding fails, the signed content MUST be rejected.
|
||||
2) The output of the base64url decoding MUST be a 64 byte array.
|
||||
3) Split the 64 byte array into two 32 byte arrays. The first array will be R and
|
||||
the second S. Remember that the byte arrays are in big endian byte order;
|
||||
please check the ECDSA validator in use to see what byte order it requires.
|
||||
4) Submit the UTF-8 representation of the JWS Signing Input, R, S and the public
|
||||
key (x, y) to the ECDSA P-256 SHA-256 validator.
|
||||
5) If the validation fails, the signed content MUST be rejected.
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
package org.mitre.jwt.signer.impl;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.InvalidKeyException;
|
||||
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;
|
||||
|
||||
public class Hmac256Signer extends AbstractJwtSigner {
|
||||
|
||||
private Mac mac;
|
||||
|
||||
private byte[] passphrase;
|
||||
|
||||
/**
|
||||
* Create a signer with no passphrase
|
||||
*/
|
||||
public Hmac256Signer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signer with the given passphrase
|
||||
* @param passphrase
|
||||
*/
|
||||
public Hmac256Signer(byte[] passphrase) {
|
||||
super(HS256);
|
||||
|
||||
//TODO: set up a factory for other signature methods
|
||||
|
||||
setPassphrase(passphrase);
|
||||
|
||||
try {
|
||||
mac = Mac.getInstance("HMACSHA256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateSignature(String signatureBase) {
|
||||
if (passphrase == null) {
|
||||
return null; // TODO: probably throw some kind of exception
|
||||
}
|
||||
|
||||
try {
|
||||
mac.init(new SecretKeySpec(getPassphrase(), mac.getAlgorithm()));
|
||||
} catch (InvalidKeyException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
mac.update(signatureBase.getBytes("UTF-8"));
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
byte[] sigBytes = mac.doFinal();
|
||||
|
||||
String sig = new String(Base64.encodeBase64URLSafe(sigBytes));
|
||||
|
||||
// strip off any padding
|
||||
sig = sig.replace("=", "");
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the passphrase
|
||||
*/
|
||||
public byte[] getPassphrase() {
|
||||
return passphrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param passphrase the passphrase to set
|
||||
*/
|
||||
public void setPassphrase(byte[] passphrase) {
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
package org.mitre.jwt.signer.impl;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||
|
||||
/**
|
||||
* JWT Signer using either the HMAC SHA-256, SHA-384, SHA-512 hash algorithm
|
||||
*
|
||||
* @author AANGANES, nemonik
|
||||
*
|
||||
*/
|
||||
public class HmacSigner extends AbstractJwtSigner {
|
||||
|
||||
/**
|
||||
* an enum for mapping a JWS name to standard algorithm name
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public enum Algorithm {
|
||||
|
||||
// Algorithm constants
|
||||
HS256("HMACSHA256"), HS384("HMACSHA384"), HS512("HMACSHA512");
|
||||
|
||||
public static final String DEFAULT = Algorithm.HS256.toString();
|
||||
|
||||
/**
|
||||
* Returns the Algorithm for the name
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static Algorithm getByName(String name) {
|
||||
for (Algorithm correspondingType : Algorithm.values()) {
|
||||
if (correspondingType.toString().equals(name)) {
|
||||
return correspondingType;
|
||||
}
|
||||
}
|
||||
|
||||
// corresponding type not found
|
||||
throw new IllegalArgumentException(
|
||||
"Algorithm name does not have a corresponding Algorithm");
|
||||
}
|
||||
|
||||
private final String standardName;
|
||||
|
||||
/**
|
||||
* Constructor of Algorithm
|
||||
*
|
||||
* @param standardName
|
||||
*/
|
||||
Algorithm(String standardName) {
|
||||
this.standardName = standardName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Java standard algorithm name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getStandardName() {
|
||||
return standardName;
|
||||
}
|
||||
};
|
||||
|
||||
private static Log logger = LogFactory.getLog(HmacSigner.class);
|
||||
|
||||
private Mac mac;
|
||||
|
||||
private String passphrase;
|
||||
|
||||
/**
|
||||
* Create a signer with no passphrase
|
||||
*/
|
||||
public HmacSigner() {
|
||||
super(Algorithm.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HMAC singer with default algorithm and passphrase as raw bytes
|
||||
*
|
||||
* @param passphraseAsRawBytes
|
||||
*/
|
||||
public HmacSigner(byte[] passphraseAsRawBytes) {
|
||||
this(Algorithm.DEFAULT, new String(passphraseAsRawBytes,
|
||||
Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HMAC singer with default algorithm and passphrase
|
||||
*
|
||||
* @param passwordAsRawBytes
|
||||
*/
|
||||
public HmacSigner(String passphrase) {
|
||||
this(Algorithm.DEFAULT, passphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HMAC singer with given algorithm and password as raw bytes
|
||||
*
|
||||
* @param algorithmName
|
||||
* the JWS name for the standard name of the requested MAC
|
||||
* algorithm
|
||||
* @param passphraseAsRawBytes
|
||||
*/
|
||||
public HmacSigner(String algorithmName, byte[] passphraseAsRawBytes) {
|
||||
this(algorithmName, new String(passphraseAsRawBytes,
|
||||
Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HMAC singer with given algorithm and passwords
|
||||
*
|
||||
* @param algorithmName
|
||||
* the JWS name for the standard name of the requested MAC
|
||||
* algorithm
|
||||
* @param passphrase
|
||||
* the passphrase
|
||||
*/
|
||||
public HmacSigner(String algorithmName, String passphrase) {
|
||||
super(algorithmName);
|
||||
|
||||
setPassphrase(passphrase);
|
||||
|
||||
try {
|
||||
mac = Mac.getInstance(Algorithm.getByName(algorithmName)
|
||||
.getStandardName());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.mitre.jwt.signer.AbstractJwtSigner#generateSignature(java.lang.String
|
||||
* )
|
||||
*/
|
||||
@Override
|
||||
protected String generateSignature(String signatureBase) {
|
||||
if (passphrase == null) {
|
||||
return null; // TODO: probably throw some kind of exception
|
||||
}
|
||||
|
||||
try {
|
||||
mac.init(new SecretKeySpec(getPassphrase().getBytes(), mac
|
||||
.getAlgorithm()));
|
||||
} catch (InvalidKeyException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
mac.update(signatureBase.getBytes("UTF-8"));
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
if (passphrase.isEmpty())
|
||||
throw new IllegalArgumentException("passphrase must be set");
|
||||
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HmacSigner [mac=" + mac + ", passphrase=" + passphrase + "]";
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ import org.mitre.jwt.signer.AbstractJwtSigner;
|
|||
|
||||
public class PlaintextSigner extends AbstractJwtSigner {
|
||||
|
||||
public static final String PLAINTEXT = "none";
|
||||
|
||||
public PlaintextSigner() {
|
||||
super(PLAINTEXT);
|
||||
}
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
package org.mitre.jwt.signer.impl;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* JWT Signer using RSA SHA-256 algorithm
|
||||
* @author AANGANES
|
||||
*
|
||||
*/
|
||||
public class Rs256Signer extends AbstractJwtSigner {
|
||||
|
||||
private PrivateKey privateKey;
|
||||
private PublicKey publicKey;
|
||||
|
||||
private Signature signer;
|
||||
|
||||
public Rs256Signer() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public Rs256Signer(PublicKey publicKey, PrivateKey privateKey) {
|
||||
super("RS256");
|
||||
|
||||
setPublicKey(publicKey);
|
||||
setPrivateKey(privateKey);
|
||||
|
||||
try {
|
||||
signer = Signature.getInstance("SHA256withRSA");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateSignature(String signatureBase) {
|
||||
|
||||
try {
|
||||
signer.initSign(privateKey);
|
||||
} catch (InvalidKeyException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
signer.update(signatureBase.getBytes("UTF-8"));
|
||||
} catch (SignatureException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
byte[] sigBytes;
|
||||
String sig = "";
|
||||
|
||||
try {
|
||||
sigBytes = signer.sign();
|
||||
sig = new String(Base64.encodeBase64URLSafe(sigBytes));
|
||||
// strip off any padding
|
||||
sig = sig.replace("=", "");
|
||||
} catch (SignatureException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String jwtString) {
|
||||
|
||||
// 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 {
|
||||
signer.initVerify(publicKey);
|
||||
} catch (InvalidKeyException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
signer.update(signingInput.getBytes("UTF-8"));
|
||||
} catch (SignatureException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
signer.verify(s64.getBytes("UTF-8"));
|
||||
} catch (SignatureException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public void setPrivateKey(PrivateKey privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
public PublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(PublicKey publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
package org.mitre.jwt.signer.impl;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||
import org.mitre.jwt.signer.service.impl.KeyStore;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* an enum for mapping a JWS name to standard algorithm name
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public enum Algorithm {
|
||||
|
||||
//Algorithm constants
|
||||
RS256("SHA256withRSA"),
|
||||
RS384("SHA384withRSA"),
|
||||
RS512("SHA512withRSA");
|
||||
|
||||
public static final String DEFAULT = Algorithm.RS256.toString();
|
||||
|
||||
/**
|
||||
* Returns the Algorithm for the name
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static Algorithm getByName(String name) {
|
||||
for (Algorithm correspondingType : Algorithm.values()) {
|
||||
if (correspondingType.toString().equals(name)) {
|
||||
return correspondingType;
|
||||
}
|
||||
}
|
||||
|
||||
// corresponding type not found
|
||||
throw new IllegalArgumentException("Algorithm name does not have a corresponding Algorithm");
|
||||
}
|
||||
|
||||
private final String standardName;
|
||||
|
||||
/**
|
||||
* Constructor of Algorithm
|
||||
*
|
||||
* @param standardName
|
||||
*/
|
||||
Algorithm(String standardName) {
|
||||
this.standardName = standardName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Java standard algorithm name
|
||||
* @return
|
||||
*/
|
||||
public String getStandardName() {
|
||||
return standardName;
|
||||
}
|
||||
};
|
||||
|
||||
private static Log logger = LogFactory.getLog(RsaSigner.class);
|
||||
|
||||
public static final String DEFAULT_PASSWORD = "changeit";
|
||||
|
||||
private KeyStore keystore;
|
||||
private String alias;
|
||||
private String password;
|
||||
|
||||
private PrivateKey privateKey;
|
||||
private PublicKey publicKey;
|
||||
private Signature signer;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public RsaSigner() {
|
||||
this(Algorithm.DEFAULT, null, null, DEFAULT_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param algorithmName
|
||||
* @param keystore
|
||||
* @param alias
|
||||
*/
|
||||
public RsaSigner(String algorithmName, KeyStore keystore, String alias) {
|
||||
this(algorithmName, keystore, alias, DEFAULT_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param algorithmName
|
||||
* @param keystore
|
||||
* @param alias
|
||||
* @param password
|
||||
*/
|
||||
public RsaSigner(String algorithmName, KeyStore keystore, String alias, String password) {
|
||||
super(algorithmName);
|
||||
|
||||
setKeystore(keystore);
|
||||
setAlias(alias);
|
||||
setPassword(password);
|
||||
|
||||
try {
|
||||
signer = Signature.getInstance(Algorithm.getByName(algorithmName).getStandardName());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
KeyPair keyPair = keystore.getKeyPairForAlias(alias, password);
|
||||
|
||||
publicKey = keyPair.getPublic();
|
||||
privateKey = keyPair.getPrivate();
|
||||
|
||||
logger.debug("RSA Signer ready for business");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateSignature(String signatureBase) {
|
||||
|
||||
try {
|
||||
signer.initSign(privateKey);
|
||||
} catch (InvalidKeyException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
signer.update(signatureBase.getBytes("UTF-8"));
|
||||
} catch (SignatureException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
byte[] sigBytes;
|
||||
String sig = "";
|
||||
|
||||
try {
|
||||
sigBytes = signer.sign();
|
||||
sig = new String(Base64.encodeBase64URLSafe(sigBytes));
|
||||
// strip off any padding
|
||||
sig = sig.replace("=", "");
|
||||
} catch (SignatureException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public KeyStore getKeystore() {
|
||||
return keystore;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* (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 + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String jwtString) {
|
||||
|
||||
// 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 {
|
||||
signer.initVerify(publicKey);
|
||||
} catch (InvalidKeyException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
signer.update(signingInput.getBytes("UTF-8"));
|
||||
} catch (SignatureException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
// TODO Auto-generated catch block
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
signer.verify(s64.getBytes("UTF-8"));
|
||||
} catch (SignatureException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.mitre.jwt.service;
|
||||
package org.mitre.jwt.signer.service;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.List;
|
||||
|
@ -9,29 +9,38 @@ public interface JwtSigningAndValidationService {
|
|||
|
||||
/**
|
||||
* Returns all public keys this service is configured with.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<PublicKey> getAllPublicKeys();
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Check to see if this JWT has expired or not
|
||||
*
|
||||
* @param jwt
|
||||
* the JWT to check
|
||||
* @return true if this JWT has an expiration and it has passed, false if
|
||||
* the JWT has no expiration or it has an expiration and the
|
||||
* expiration has not passed
|
||||
*/
|
||||
public boolean validateSignature(String jwtString);
|
||||
|
||||
public boolean isJwtExpired(Jwt jwt);
|
||||
|
||||
/**
|
||||
* Checks to see if this JWT has been issued by us
|
||||
* @param jwt the JWT to check the issuer of
|
||||
*
|
||||
* @param jwt
|
||||
* the JWT to check the issuer of
|
||||
* @return true if the JWT was issued by this AS, false if not
|
||||
*/
|
||||
public boolean validateIssuedJwt(Jwt jwt);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check to see if this JWT has expired or not
|
||||
* @param jwt the JWT to check
|
||||
* @return true if this JWT has an expiration and it has passed, false if the JWT has no expiration or it has an expiration and the expiration has not passed
|
||||
* 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
|
||||
*/
|
||||
public boolean isJwtExpired(Jwt jwt);
|
||||
public boolean validateSignature(String jwtString);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* Support class for implementing custom jwt-signer namespace
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class JwtSignerNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
|
||||
*/
|
||||
@Override
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("keystore", new KeystoreDefinitionParser());
|
||||
registerBeanDefinitionParser("service", new ServiceDefinitionParser());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.mitre.jwt.model.Jwt;
|
||||
import org.mitre.jwt.signer.JwtSigner;
|
||||
import org.mitre.jwt.signer.impl.EcdsaSigner;
|
||||
import org.mitre.jwt.signer.impl.RsaSigner;
|
||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
public class JwtSigningAndValidationServiceDefault implements
|
||||
JwtSigningAndValidationService, InitializingBean {
|
||||
|
||||
private List<? extends JwtSigner> signers = new ArrayList<JwtSigner>();
|
||||
|
||||
private static Log logger = LogFactory
|
||||
.getLog(JwtSigningAndValidationServiceDefault.class);
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
*/
|
||||
public JwtSigningAndValidationServiceDefault() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JwtSigningAndValidationServiceDefault
|
||||
*
|
||||
* @param signer List of JwtSigners to associate with this service
|
||||
*/
|
||||
public JwtSigningAndValidationServiceDefault(List<? extends JwtSigner> signer) {
|
||||
setSigners(signer);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// used for debugging...
|
||||
if (!signers.isEmpty()) {
|
||||
logger.info(this.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.mitre.jwt.signer.service.JwtSigningAndValidationService#getAllPublicKeys
|
||||
* ()
|
||||
*/
|
||||
@Override
|
||||
public List<PublicKey> getAllPublicKeys() {
|
||||
// TODO Iterate through the signers, gather up, and return all the PublicKeys
|
||||
|
||||
List<PublicKey> publicKeys = new ArrayList<PublicKey>();
|
||||
PublicKey publicKey;
|
||||
|
||||
for (JwtSigner signer: signers) {
|
||||
|
||||
if (signer instanceof RsaSigner) {
|
||||
|
||||
publicKey = ((RsaSigner) signer).getPublicKey();
|
||||
|
||||
if (publicKey != null)
|
||||
publicKeys.add(((RsaSigner) signer).getPublicKey());
|
||||
|
||||
} else if (signer instanceof EcdsaSigner) {
|
||||
|
||||
publicKey = ((EcdsaSigner) signer).getPublicKey();
|
||||
|
||||
if (publicKey != null)
|
||||
publicKeys.add(publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
return publicKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JwtSigners associated with this service
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<? extends JwtSigner> getSigners() {
|
||||
return signers;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.mitre.jwt.signer.service.JwtSigningAndValidationService#isJwtExpired
|
||||
* (org.mitre.jwt.model.Jwt)
|
||||
*/
|
||||
@Override
|
||||
public boolean isJwtExpired(Jwt jwt) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JwtSigners associated with this service
|
||||
*
|
||||
* @param signers
|
||||
* List of JwtSigners to associate with this service
|
||||
*/
|
||||
public void setSigners(List<? extends JwtSigner> signers) {
|
||||
this.signers = signers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JwtSigningAndValidationServiceDefault [signers=" + signers
|
||||
+ "]";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.mitre.jwt.signer.service.JwtSigningAndValidationService#validateIssuedJwt
|
||||
* (org.mitre.jwt.model.Jwt)
|
||||
*/
|
||||
@Override
|
||||
public boolean validateIssuedJwt(Jwt jwt) {
|
||||
|
||||
// TODO Verify this is correct...
|
||||
|
||||
for (JwtSigner signer: signers) {
|
||||
if (signer.verify(jwt.toString()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.mitre.jwt.signer.service.JwtSigningAndValidationService#validateSignature
|
||||
* (java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean validateSignature(String jwtString) {
|
||||
|
||||
for (JwtSigner signer: signers) {
|
||||
if (signer.verify(jwtString))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Creates and manages a JCE KeyStore
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class KeyStore implements InitializingBean {
|
||||
|
||||
// TODO: Doesn't have a provider attribute, getter/setter. Not sure we need.
|
||||
|
||||
private static Log logger = LogFactory.getLog(KeyStore.class);
|
||||
|
||||
private static final String TYPE = java.security.KeyStore.getDefaultType();
|
||||
private static final String PASSWORD = "changeit";
|
||||
|
||||
private String password;
|
||||
private Resource location;
|
||||
private String type;
|
||||
|
||||
private java.security.KeyStore keystore;
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
*/
|
||||
public KeyStore() {
|
||||
this(PASSWORD, null, TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param password
|
||||
* @param location
|
||||
*/
|
||||
public KeyStore(String password, Resource location) {
|
||||
this(password, location, TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyStore constructor
|
||||
*
|
||||
* @param password
|
||||
* the password used to unlock the keystore
|
||||
* @param location
|
||||
* the location of the keystore
|
||||
* @param type
|
||||
* the type of keystore. See Appendix A in the Java Cryptography
|
||||
* Architecture API Specification & Reference for information
|
||||
* about standard keystore types.
|
||||
*/
|
||||
public KeyStore(String password, Resource location, String type) {
|
||||
setPassword(password);
|
||||
setLocation(location);
|
||||
setType(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws IOException,
|
||||
GeneralSecurityException {
|
||||
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
keystore = java.security.KeyStore.getInstance(type);
|
||||
inputStream = location.getInputStream();
|
||||
keystore.load(inputStream, this.password.toCharArray());
|
||||
|
||||
logger.info("Loaded keystore from " + location);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a KeyPair for the alias given the password
|
||||
*
|
||||
* @param alias
|
||||
* the alias name
|
||||
* @param password
|
||||
* the password for recovering the key pair
|
||||
* @return the key pair
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
public KeyPair getKeyPairForAlias(String alias, String password)
|
||||
throws GeneralSecurityException {
|
||||
|
||||
Key key = keystore.getKey(alias, password.toCharArray());
|
||||
|
||||
if (key instanceof PrivateKey) {
|
||||
|
||||
// Get certificate of public key
|
||||
java.security.cert.Certificate cert = keystore
|
||||
.getCertificate(alias);
|
||||
|
||||
// Get public key
|
||||
PublicKey publicKey = cert.getPublicKey();
|
||||
|
||||
return new KeyPair(publicKey, (PrivateKey) key);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Resource getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setLocation(Resource location) {
|
||||
if (location != null && location.exists()) {
|
||||
this.location = location;
|
||||
} else {
|
||||
throw new IllegalArgumentException("location must exist");
|
||||
}
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
if (StringUtils.hasLength(type)) {
|
||||
this.type = type;
|
||||
} else {
|
||||
throw new IllegalArgumentException("type must not be empty");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KeyStore [password=" + password + ", location=" + location
|
||||
+ ", type=" + type + ", keystore=" + keystore + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Needed to parse and define just a single BeanDefinition for the KeyStore
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class KeystoreDefinitionParser extends
|
||||
AbstractSingleBeanDefinitionParser {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
|
||||
* #doParse(org.w3c.dom.Element,
|
||||
* org.springframework.beans.factory.xml.ParserContext,
|
||||
* org.springframework.beans.factory.support.BeanDefinitionBuilder)
|
||||
*/
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext,
|
||||
BeanDefinitionBuilder builder) {
|
||||
|
||||
String password = element.getAttribute("password");
|
||||
if (StringUtils.hasText(password)) {
|
||||
builder.addConstructorArgValue(password);
|
||||
}
|
||||
|
||||
String location = element.getAttribute("location");
|
||||
|
||||
if (!StringUtils.hasText(location)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"A location must be supplied on a keystore element.",
|
||||
element);
|
||||
} else {
|
||||
|
||||
Resource resource = parserContext.getReaderContext().getResourceLoader().getResource(location);
|
||||
|
||||
if (!resource.exists()) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The location supplied on the keystore element must exist.",
|
||||
element);
|
||||
} else {
|
||||
builder.addConstructorArgValue(resource);
|
||||
}
|
||||
}
|
||||
|
||||
String type = element.getAttribute("type");
|
||||
if (StringUtils.hasText(type)) {
|
||||
builder.addConstructorArgValue(type);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
|
||||
* #getBeanClass(org.w3c.dom.Element)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return KeyStore.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.mitre.jwt.signer.impl.HmacSigner;
|
||||
import org.mitre.jwt.signer.impl.RsaSigner;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Needed to parse and define just a single BeanDefinition for the
|
||||
* JwtSigningAndValidationServiceDefault
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class ServiceDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ServiceDefinitionParser.class);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
|
||||
* #doParse(org.w3c.dom.Element,
|
||||
* org.springframework.beans.factory.xml.ParserContext,
|
||||
* org.springframework.beans.factory.support.BeanDefinitionBuilder)
|
||||
*/
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext,
|
||||
BeanDefinitionBuilder builder) {
|
||||
|
||||
ManagedList<BeanMetadataElement> signers = new ManagedList<BeanMetadataElement>();
|
||||
|
||||
List<Element> signerElements = DomUtils.getChildElementsByTagName(
|
||||
element, new String[] { "rsa", "hmac" });
|
||||
|
||||
for (Element signerElement : signerElements) {
|
||||
|
||||
if (signerElement.getTagName().contains("rsa")) {
|
||||
|
||||
logger.debug("parsing rsa element");
|
||||
|
||||
BeanDefinitionBuilder signer = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RsaSigner.class);
|
||||
|
||||
String bits = signerElement.getAttribute("bits");
|
||||
if (StringUtils.hasText(bits)) {
|
||||
signer.addConstructorArgValue("RS".concat(bits));
|
||||
} else {
|
||||
signer.addConstructorArgValue(RsaSigner.Algorithm.DEFAULT);
|
||||
}
|
||||
|
||||
String keystoreRef = signerElement.getAttribute("keystore-ref");
|
||||
if (!StringUtils.hasText(keystoreRef)) {
|
||||
parserContext
|
||||
.getReaderContext()
|
||||
.error("A keystore-ref must be supplied with the definition of a rsa.",
|
||||
signerElement);
|
||||
} else {
|
||||
signer.addConstructorArgReference(keystoreRef);
|
||||
}
|
||||
|
||||
String alias = signerElement.getAttribute("key-alias");
|
||||
if (!StringUtils.hasText(alias)) {
|
||||
parserContext
|
||||
.getReaderContext()
|
||||
.error("An key-alias must be supplied with the definition of a rsa.",
|
||||
signerElement);
|
||||
} else {
|
||||
signer.addConstructorArgValue(alias);
|
||||
}
|
||||
|
||||
String password = signerElement.getAttribute("password");
|
||||
if (StringUtils.hasText(password)) {
|
||||
signer.addConstructorArgValue(password);
|
||||
} else {
|
||||
signer.addConstructorArgValue(RsaSigner.DEFAULT_PASSWORD);
|
||||
}
|
||||
|
||||
signers.add(signer.getBeanDefinition());
|
||||
|
||||
} else if (signerElement.getTagName().contains("hmac")) {
|
||||
|
||||
logger.debug("parsing hmac element");
|
||||
|
||||
BeanDefinitionBuilder signer = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(HmacSigner.class);
|
||||
|
||||
String bits = signerElement.getAttribute("bits");
|
||||
if (StringUtils.hasText(bits)) {
|
||||
signer.addConstructorArgValue("HS".concat(bits));
|
||||
} else {
|
||||
signer.addConstructorArgValue(HmacSigner.Algorithm.DEFAULT);
|
||||
}
|
||||
|
||||
String passphrase = signerElement.getAttribute("passphrase");
|
||||
if (!StringUtils.hasText(passphrase)) {
|
||||
parserContext
|
||||
.getReaderContext()
|
||||
.error("A passphrase must be supplied with the definition of a hmac.",
|
||||
signerElement);
|
||||
} else {
|
||||
signer.addConstructorArgValue(passphrase);
|
||||
}
|
||||
|
||||
signers.add(signer.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
builder.addPropertyValue("signers", signers);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
|
||||
* #getBeanClass(org.w3c.dom.Element)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return JwtSigningAndValidationServiceDefault.class;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package org.mitre.openid.connect.web;
|
||||
|
||||
import org.mitre.jwt.service.JwtSigningAndValidationService;
|
||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
||||
import org.mitre.openid.connect.exception.ExpiredTokenException;
|
||||
import org.mitre.openid.connect.exception.InvalidJwtIssuerException;
|
||||
import org.mitre.openid.connect.exception.InvalidJwtSignatureException;
|
||||
|
|
|
@ -5,7 +5,7 @@ import java.security.interfaces.ECPublicKey;
|
|||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.List;
|
||||
|
||||
import org.mitre.jwt.service.JwtSigningAndValidationService;
|
||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
http\://www.mitre.org/schema/openid-connect/jwt-signer=org.mitre.jwt.signer.service.impl.JwtSignerNamespaceHandler
|
|
@ -0,0 +1 @@
|
|||
http\://www.mitre.org/schema/openid-connect/jwt-signer/jwt-signer.xsd=org/mitre/jwt/signer/service/impl/jwt-signer.xsd
|
|
@ -0,0 +1,94 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<xs:schema xmlns="http://www.mitre.org/schema/openid-connect/jwt-signer"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
targetNamespace="http://www.mitre.org/schema/openid-connect/jwt-signer"
|
||||
elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||
|
||||
<xs:import namespace="http://www.springframework.org/schema/beans" />
|
||||
|
||||
<xs:element name="keystore">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Describes the JCE KeyStore necessary for certain
|
||||
signers.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="beans:identifiedType">
|
||||
<xs:attribute name="location" type="xs:string" use="required" />
|
||||
<xs:attribute name="password" type="xs:string" />
|
||||
<xs:attribute name="type" type="xs:string" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="service">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Configures the signer service with these signers.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="beans:identifiedType">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="rsa">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Configures an RSA signer.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attribute name="bits" type="xs:string" />
|
||||
<xs:attribute name="keystore-ref" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The reference to the bean that defines the
|
||||
KeyStore.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="key-alias" type="xs:string"
|
||||
use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The alias to the KeyPair to use for
|
||||
signing/verifying.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="password" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The password to the KeyPair to use for
|
||||
signing/verifying.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="hmac">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Configures an HMAC signer.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attribute name="bits" type="xs:integer" />
|
||||
<xs:attribute name="passphrase" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The passphrase used for signing/verifying.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
|
@ -5,6 +5,7 @@
|
|||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:jwt-signer="http://www.mitre.org/schema/openid-connect/jwt-signer"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xsi:schemaLocation=
|
||||
"http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
|
||||
|
@ -12,9 +13,11 @@
|
|||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.mitre.org/schema/openid-connect/jwt-signer http://www.mitre.org/schema/openid-connect/jwt-signer/jwt-signer.xsd">
|
||||
|
||||
<import resource="data-context.xml" />
|
||||
|
||||
<import resource="security-context.xml" />
|
||||
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
@ -70,7 +73,15 @@
|
|||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<jwt-signer:keystore id="ping" location="file:src/main/webapp/WEB-INF/spring/keystore.jks" password="changeit" type="JKS" />
|
||||
|
||||
<jwt-signer:service id="defaultSignerService">
|
||||
<jwt-signer:rsa bits="256" keystore-ref="ping" key-alias="test" password="changeit" />
|
||||
<jwt-signer:hmac bits="256" passphrase="changeit" />
|
||||
</jwt-signer:service>
|
||||
|
||||
|
||||
<!-- scheduled tasks -->
|
||||
<task:scheduler id="taskScheduler" pool-size="10" />
|
||||
<task:executor id="taskExecutor" pool-size="5" />
|
||||
|
|
Binary file not shown.
|
@ -1,19 +1,16 @@
|
|||
package org.mitre.jwt;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mitre.jwt.model.Jwt;
|
||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||
import org.mitre.jwt.signer.JwtSigner;
|
||||
import org.mitre.jwt.signer.impl.Hmac256Signer;
|
||||
import org.mitre.jwt.signer.impl.HmacSigner;
|
||||
import org.mitre.jwt.signer.impl.PlaintextSigner;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
public class JwtTest {
|
||||
|
||||
|
@ -60,8 +57,8 @@ public class JwtTest {
|
|||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
JwtSigner signer = new Hmac256Signer(key);
|
||||
|
||||
JwtSigner signer = new HmacSigner(key);
|
||||
|
||||
signer.sign(jwt);
|
||||
|
||||
|
@ -95,7 +92,7 @@ public class JwtTest {
|
|||
e.printStackTrace();
|
||||
}
|
||||
|
||||
JwtSigner signer = new Hmac256Signer(key);
|
||||
JwtSigner signer = new HmacSigner(key);
|
||||
|
||||
/*
|
||||
* Token string based on the following strucutres, serialized exactly as follows and base64 encoded:
|
||||
|
@ -121,7 +118,7 @@ public class JwtTest {
|
|||
|
||||
Jwt jwt = Jwt.parse(source);
|
||||
|
||||
assertThat(jwt.getHeader().getAlgorithm(), equalTo(AbstractJwtSigner.PLAINTEXT));
|
||||
assertThat(jwt.getHeader().getAlgorithm(), equalTo(PlaintextSigner.PLAINTEXT));
|
||||
assertThat(jwt.getClaims().getIssuer(), equalTo("joe"));
|
||||
assertThat(jwt.getClaims().getExpiration(), equalTo(new Date(1300819380L * 1000L)));
|
||||
assertThat((Boolean)jwt.getClaims().getClaim("http://example.com/is_root"), equalTo(Boolean.TRUE));
|
||||
|
|
Loading…
Reference in New Issue