merged from remote, regenerated keystore
commit
71292bcefd
|
@ -1,18 +1,23 @@
|
||||||
package org.mitre.jwt.signer.impl;
|
package org.mitre.jwt.signer.impl;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivateKey;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.Security;
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.mitre.jwt.signer.AbstractJwtSigner;
|
import org.mitre.jwt.signer.AbstractJwtSigner;
|
||||||
import org.mitre.jwt.signer.service.impl.KeyStore;
|
import org.mitre.jwt.signer.service.impl.KeyStore;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
@ -24,7 +29,7 @@ import com.google.common.collect.Lists;
|
||||||
* JWT Signer using either the RSA SHA-256, SHA-384, SHA-512 hash algorithm
|
* JWT Signer using either the RSA SHA-256, SHA-384, SHA-512 hash algorithm
|
||||||
*
|
*
|
||||||
* @author AANGANES, nemonik
|
* @author AANGANES, nemonik
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
|
|
||||||
|
@ -32,36 +37,36 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
* an enum for mapping a JWS name to standard algorithm name
|
* an enum for mapping a JWS name to standard algorithm name
|
||||||
*
|
*
|
||||||
* @author nemonik
|
* @author nemonik
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public enum Algorithm {
|
public enum Algorithm {
|
||||||
|
|
||||||
//Algorithm constants
|
// Algorithm constants
|
||||||
RS256("SHA256withRSA"),
|
RS256("SHA256withRSA"), RS384("SHA384withRSA"), RS512("SHA512withRSA");
|
||||||
RS384("SHA384withRSA"),
|
|
||||||
RS512("SHA512withRSA");
|
|
||||||
|
|
||||||
public static final String DEFAULT = Algorithm.RS256.toString();
|
public static final String DEFAULT = Algorithm.RS256.toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Algorithm for the name
|
* Returns the Algorithm for the name
|
||||||
* @param name
|
*
|
||||||
* @return
|
* @param name
|
||||||
*/
|
* @return
|
||||||
public static Algorithm getByName(String name) {
|
*/
|
||||||
for (Algorithm correspondingType : Algorithm.values()) {
|
public static Algorithm getByName(String name) {
|
||||||
if (correspondingType.toString().equals(name)) {
|
for (Algorithm correspondingType : Algorithm.values()) {
|
||||||
return correspondingType;
|
if (correspondingType.toString().equals(name)) {
|
||||||
}
|
return correspondingType;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// corresponding type not found
|
|
||||||
throw new IllegalArgumentException("Algorithm name does not have a corresponding Algorithm");
|
// corresponding type not found
|
||||||
}
|
throw new IllegalArgumentException(
|
||||||
|
"Algorithm name does not have a corresponding Algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
private final String standardName;
|
private final String standardName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of Algorithm
|
* Constructor of Algorithm
|
||||||
*
|
*
|
||||||
* @param standardName
|
* @param standardName
|
||||||
|
@ -69,26 +74,31 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
Algorithm(String standardName) {
|
Algorithm(String standardName) {
|
||||||
this.standardName = standardName;
|
this.standardName = standardName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Java standard algorithm name
|
* Return the Java standard algorithm name
|
||||||
* @return
|
*
|
||||||
*/
|
* @return
|
||||||
public String getStandardName() {
|
*/
|
||||||
return standardName;
|
public String getStandardName() {
|
||||||
}
|
return standardName;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(RsaSigner.class);
|
private static Log logger = LogFactory.getLog(RsaSigner.class);
|
||||||
|
|
||||||
public static final String DEFAULT_PASSWORD = "changeit";
|
public static final String DEFAULT_PASSWORD = "changeit";
|
||||||
|
|
||||||
|
static {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
private KeyStore keystore;
|
private KeyStore keystore;
|
||||||
private String alias;
|
private String alias;
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private PrivateKey privateKey;
|
private RSAPrivateKey privateKey;
|
||||||
private PublicKey publicKey;
|
private RSAPublicKey publicKey;
|
||||||
private Signature signer;
|
private Signature signer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,58 +116,56 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
public RsaSigner(String algorithmName, KeyStore keystore, String alias) {
|
public RsaSigner(String algorithmName, KeyStore keystore, String alias) {
|
||||||
this(algorithmName, keystore, alias, DEFAULT_PASSWORD);
|
this(algorithmName, keystore, alias, DEFAULT_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param algorithmName
|
* @param algorithmName
|
||||||
* @param keystore
|
* @param keystore
|
||||||
* @param alias
|
* @param alias
|
||||||
* @param password
|
* @param password
|
||||||
*/
|
*/
|
||||||
public RsaSigner(String algorithmName, KeyStore keystore, String alias, String password) {
|
public RsaSigner(String algorithmName, KeyStore keystore, String alias,
|
||||||
|
String password) {
|
||||||
super(algorithmName);
|
super(algorithmName);
|
||||||
|
|
||||||
setKeystore(keystore);
|
setKeystore(keystore);
|
||||||
setAlias(alias);
|
setAlias(alias);
|
||||||
setPassword(password);
|
setPassword(password);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signer = Signature.getInstance(Algorithm.getByName(algorithmName).getStandardName());
|
signer = Signature.getInstance(Algorithm.getByName(algorithmName).getStandardName(), "BC");
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
KeyPair keyPair = keystore.getKeyPairForAlias(alias, password);
|
KeyPair keyPair = keystore.getKeyPairForAlias(alias, password);
|
||||||
|
|
||||||
publicKey = keyPair.getPublic();
|
publicKey = ((RSAPublicKey) keyPair.getPublic());
|
||||||
privateKey = keyPair.getPrivate();
|
privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||||
|
|
||||||
logger.debug("RSA Signer ready for business");
|
logger.debug("RSA Signer ready for business");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see org.mitre.jwt.signer.AbstractJwtSigner#generateSignature(java.lang.String)
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String generateSignature(String signatureBase) {
|
protected String generateSignature(String signatureBase) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signer.initSign(privateKey);
|
signer.initSign(privateKey);
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
signer.update(signatureBase.getBytes("UTF-8"));
|
signer.update(signatureBase.getBytes("UTF-8"));
|
||||||
} catch (SignatureException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] sigBytes;
|
byte[] sigBytes;
|
||||||
String sig = "";
|
String sig = "";
|
||||||
|
@ -178,7 +186,7 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
public String getAlias() {
|
public String getAlias() {
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyStore getKeystore() {
|
public KeyStore getKeystore() {
|
||||||
return keystore;
|
return keystore;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +211,9 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
*
|
||||||
* @see java.lang.Object#toString()
|
* @see java.lang.Object#toString()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -232,34 +242,24 @@ public class RsaSigner extends AbstractJwtSigner implements InitializingBean {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
signer.initVerify(publicKey);
|
signer.initVerify(publicKey);
|
||||||
} catch (InvalidKeyException e1) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e1.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
signer.update(signingInput.getBytes("UTF-8"));
|
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"));
|
signer.verify(s64.getBytes("UTF-8"));
|
||||||
} catch (SignatureException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RSAPrivateKey getPrivateKey() {
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(RSAPrivateKey privateKey) {
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,35 @@
|
||||||
package org.mitre.jwt.signer.service.impl;
|
package org.mitre.jwt.signer.service.impl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.bouncycastle.jce.X509Principal;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.x509.X509V3CertificateGenerator;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and manages a JCE KeyStore
|
* Creates and manages a JCE KeyStore
|
||||||
|
@ -20,18 +37,164 @@ import org.springframework.util.StringUtils;
|
||||||
* @author nemonik
|
* @author nemonik
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class KeyStore implements InitializingBean {
|
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 Log logger = LogFactory.getLog(KeyStore.class);
|
||||||
|
|
||||||
private static final String TYPE = java.security.KeyStore.getDefaultType();
|
public static final String TYPE = "BKS";
|
||||||
private static final String PASSWORD = "changeit";
|
public static final String PASSWORD = "changeit";
|
||||||
|
|
||||||
|
static {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a certificate.
|
||||||
|
*
|
||||||
|
* @param commonName
|
||||||
|
* @param daysNotValidBefore
|
||||||
|
* @param daysNotValidAfter
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static X509V3CertificateGenerator createCertificate(
|
||||||
|
String commonName, int daysNotValidBefore, int daysNotValidAfter) {
|
||||||
|
// BC docs say to use another, but it seemingly isn't included...
|
||||||
|
X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();
|
||||||
|
|
||||||
|
v3CertGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
|
||||||
|
v3CertGen.setIssuerDN(new X509Principal("CN=" + commonName
|
||||||
|
+ ", OU=None, O=None L=None, C=None"));
|
||||||
|
v3CertGen.setNotBefore(new Date(System.currentTimeMillis()
|
||||||
|
- (1000L * 60 * 60 * 24 * daysNotValidBefore)));
|
||||||
|
v3CertGen.setNotAfter(new Date(System.currentTimeMillis()
|
||||||
|
+ (1000L * 60 * 60 * 24 * daysNotValidAfter)));
|
||||||
|
v3CertGen.setSubjectDN(new X509Principal("CN=" + commonName
|
||||||
|
+ ", OU=None, O=None L=None, C=None"));
|
||||||
|
return v3CertGen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an RSA KeyPair and insert into specified KeyStore
|
||||||
|
*
|
||||||
|
* @param location
|
||||||
|
* @param domainName
|
||||||
|
* @param alias
|
||||||
|
* @param keystorePassword
|
||||||
|
* @param aliasPassword
|
||||||
|
* @param daysNotValidBefore
|
||||||
|
* @param daysNotValidAfter
|
||||||
|
* @return
|
||||||
|
* @throws GeneralSecurityException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static java.security.KeyStore generateRsaKeyPair(String location,
|
||||||
|
String domainName, String alias, String keystorePassword,
|
||||||
|
String aliasPassword, int daysNotValidBefore, int daysNotValidAfter)
|
||||||
|
throws GeneralSecurityException, IOException {
|
||||||
|
|
||||||
|
java.security.KeyStore ks = loadJceKeyStore(location, keystorePassword);
|
||||||
|
|
||||||
|
KeyPairGenerator rsaKeyPairGenerator = KeyPairGenerator
|
||||||
|
.getInstance("RSA", "BC");
|
||||||
|
rsaKeyPairGenerator.initialize(2048);
|
||||||
|
KeyPair rsaKeyPair = rsaKeyPairGenerator.generateKeyPair();
|
||||||
|
|
||||||
|
X509V3CertificateGenerator v3CertGen = createCertificate(domainName,
|
||||||
|
daysNotValidBefore, daysNotValidAfter);
|
||||||
|
|
||||||
|
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) rsaKeyPair.getPrivate();
|
||||||
|
|
||||||
|
v3CertGen.setPublicKey(rsaKeyPair.getPublic());
|
||||||
|
v3CertGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); // "MD5WithRSAEncryption");
|
||||||
|
|
||||||
|
// BC docs say to use another, but it seemingly isn't included...
|
||||||
|
X509Certificate certificate = v3CertGen
|
||||||
|
.generateX509Certificate(rsaPrivateKey);
|
||||||
|
|
||||||
|
// if exist, overwrite
|
||||||
|
ks.setKeyEntry(alias, rsaPrivateKey, aliasPassword.toCharArray(),
|
||||||
|
new java.security.cert.Certificate[] { certificate });
|
||||||
|
|
||||||
|
storeJceKeyStore(location, keystorePassword, ks);
|
||||||
|
|
||||||
|
return ks;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates or loads a JCE KeyStore
|
||||||
|
* @param location
|
||||||
|
* @param keystorePassword
|
||||||
|
* @return
|
||||||
|
* @throws GeneralSecurityException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private static java.security.KeyStore loadJceKeyStore(String location, String keystorePassword) throws GeneralSecurityException, IOException {
|
||||||
|
java.security.KeyStore ks = java.security.KeyStore.getInstance(TYPE);
|
||||||
|
|
||||||
|
File keystoreFile = new File(location);
|
||||||
|
if (!keystoreFile.exists()) {
|
||||||
|
ks.load(null, null);
|
||||||
|
} else {
|
||||||
|
InputStream ios = new FileInputStream(keystoreFile);
|
||||||
|
try {
|
||||||
|
ks.load(ios, keystorePassword.toCharArray());
|
||||||
|
logger.info("Loaded keystore from " + location);
|
||||||
|
} finally {
|
||||||
|
ios.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
//TODO create a cmd-line to create the KeyStore?
|
||||||
|
|
||||||
|
try {
|
||||||
|
KeyStore.generateRsaKeyPair("/tmp/keystore.jks",
|
||||||
|
"OpenID Connect Server", "test", KeyStore.PASSWORD,
|
||||||
|
KeyStore.PASSWORD, 30, 365);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the JCE KeyStore
|
||||||
|
*
|
||||||
|
* @param location
|
||||||
|
* @param keystorePassword
|
||||||
|
* @param ks
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
* @throws KeyStoreException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws CertificateException
|
||||||
|
*/
|
||||||
|
private static void storeJceKeyStore(String location,
|
||||||
|
String keystorePassword, java.security.KeyStore ks)
|
||||||
|
throws FileNotFoundException, KeyStoreException, IOException,
|
||||||
|
NoSuchAlgorithmException, CertificateException {
|
||||||
|
File keystoreFile = new File(location);
|
||||||
|
FileOutputStream fos = new FileOutputStream(keystoreFile);
|
||||||
|
try {
|
||||||
|
ks.store(fos, keystorePassword.toCharArray());
|
||||||
|
} finally {
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Keystore created here: " + keystoreFile.getAbsolutePath());
|
||||||
|
}
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private Resource location;
|
private Resource location;
|
||||||
private String type;
|
|
||||||
|
|
||||||
private java.security.KeyStore keystore;
|
private java.security.KeyStore keystore;
|
||||||
|
|
||||||
|
@ -39,15 +202,7 @@ public class KeyStore implements InitializingBean {
|
||||||
* default constructor
|
* default constructor
|
||||||
*/
|
*/
|
||||||
public KeyStore() {
|
public KeyStore() {
|
||||||
this(PASSWORD, null, TYPE);
|
this(PASSWORD, null);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param password
|
|
||||||
* @param location
|
|
||||||
*/
|
|
||||||
public KeyStore(String password, Resource location) {
|
|
||||||
this(password, location, TYPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,15 +212,11 @@ public class KeyStore implements InitializingBean {
|
||||||
* the password used to unlock the keystore
|
* the password used to unlock the keystore
|
||||||
* @param location
|
* @param location
|
||||||
* the location of the keystore
|
* 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) {
|
public KeyStore(String password, Resource location) {
|
||||||
setPassword(password);
|
setPassword(password);
|
||||||
setLocation(location);
|
setLocation(location);
|
||||||
setType(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -75,13 +226,12 @@ public class KeyStore implements InitializingBean {
|
||||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws IOException,
|
public void afterPropertiesSet() throws Exception {
|
||||||
GeneralSecurityException {
|
|
||||||
|
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
keystore = java.security.KeyStore.getInstance(type);
|
keystore = java.security.KeyStore.getInstance(TYPE);
|
||||||
inputStream = location.getInputStream();
|
inputStream = location.getInputStream();
|
||||||
keystore.load(inputStream, this.password.toCharArray());
|
keystore.load(inputStream, this.password.toCharArray());
|
||||||
|
|
||||||
|
@ -91,6 +241,10 @@ public class KeyStore implements InitializingBean {
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keystore.size() == 0) {
|
||||||
|
throw new Exception("Keystore is empty; it has no entries");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,12 +271,16 @@ public class KeyStore implements InitializingBean {
|
||||||
// Get public key
|
// Get public key
|
||||||
PublicKey publicKey = cert.getPublicKey();
|
PublicKey publicKey = cert.getPublicKey();
|
||||||
|
|
||||||
return new KeyPair(publicKey, (PrivateKey) key);
|
return new KeyPair(publicKey, (RSAPrivateKey) key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public java.security.KeyStore getKeystore() {
|
||||||
|
return keystore;
|
||||||
|
}
|
||||||
|
|
||||||
public Resource getLocation() {
|
public Resource getLocation() {
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
@ -131,8 +289,14 @@ public class KeyStore implements InitializingBean {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public Provider getProvider() {
|
||||||
return type;
|
return keystore.getProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void setKeystore(java.security.KeyStore keystore) {
|
||||||
|
this.keystore = keystore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(Resource location) {
|
public void setLocation(Resource location) {
|
||||||
|
@ -147,14 +311,6 @@ public class KeyStore implements InitializingBean {
|
||||||
this.password = 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)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -163,7 +319,6 @@ public class KeyStore implements InitializingBean {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "KeyStore [password=" + password + ", location=" + location
|
return "KeyStore [password=" + password + ", location=" + location
|
||||||
+ ", type=" + type + ", keystore=" + keystore + "]";
|
+ ", keystore=" + keystore + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,6 @@ public class KeystoreDefinitionParser extends
|
||||||
builder.addConstructorArgValue(resource);
|
builder.addConstructorArgValue(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String type = element.getAttribute("type");
|
|
||||||
if (StringUtils.hasText(type)) {
|
|
||||||
builder.addConstructorArgValue(type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
<xs:extension base="beans:identifiedType">
|
<xs:extension base="beans:identifiedType">
|
||||||
<xs:attribute name="location" type="xs:string" use="required" />
|
<xs:attribute name="location" type="xs:string" use="required" />
|
||||||
<xs:attribute name="password" type="xs:string" />
|
<xs:attribute name="password" type="xs:string" />
|
||||||
<xs:attribute name="type" type="xs:string" />
|
|
||||||
</xs:extension>
|
</xs:extension>
|
||||||
</xs:complexContent>
|
</xs:complexContent>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
|
@ -74,12 +74,11 @@
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<jwt-signer:keystore id="defaultKeystore" location="WEB-INF/spring/keystore.jks" password="changeit" type="JKS" />
|
<jwt-signer:keystore id="defaultKeystore" location="WEB-INF/spring/keystore.jks" password="changeit" type="JKS" />
|
||||||
|
|
||||||
<jwt-signer:service id="defaultSignerService">
|
<jwt-signer:service id="defaultSignerService">
|
||||||
<jwt-signer:rsa bits="256" keystore-ref="defaultKeystore" key-alias="test" password="changeit" />
|
<jwt-signer:rsa bits="256" keystore-ref="defaultKeystore" key-alias="test" password="changeit" />
|
||||||
<jwt-signer:hmac bits="256" passphrase="changeit" />
|
<jwt-signer:hmac bits="256" passphrase="changeit" />
|
||||||
</jwt-signer:service>
|
</jwt-signer:service>
|
||||||
|
|
||||||
|
|
||||||
<!-- scheduled tasks -->
|
<!-- scheduled tasks -->
|
||||||
<task:scheduler id="taskScheduler" pool-size="10" />
|
<task:scheduler id="taskScheduler" pool-size="10" />
|
||||||
|
|
Binary file not shown.
|
@ -7,13 +7,28 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
import org.mitre.jwt.model.Jwt;
|
import org.mitre.jwt.model.Jwt;
|
||||||
import org.mitre.jwt.signer.JwtSigner;
|
import org.mitre.jwt.signer.JwtSigner;
|
||||||
import org.mitre.jwt.signer.impl.HmacSigner;
|
import org.mitre.jwt.signer.impl.HmacSigner;
|
||||||
import org.mitre.jwt.signer.impl.PlaintextSigner;
|
import org.mitre.jwt.signer.impl.PlaintextSigner;
|
||||||
|
import org.mitre.jwt.signer.impl.RsaSigner;
|
||||||
|
import org.mitre.jwt.signer.service.impl.KeyStore;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(locations = {
|
||||||
|
"file:src/main/webapp/WEB-INF/spring/application-context.xml",
|
||||||
|
"classpath:test-context.xml" })
|
||||||
public class JwtTest {
|
public class JwtTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("testKeystore")
|
||||||
|
KeyStore keystore;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testToStringPlaintext() {
|
public void testToStringPlaintext() {
|
||||||
Jwt jwt = new Jwt();
|
Jwt jwt = new Jwt();
|
||||||
|
@ -63,7 +78,7 @@ public class JwtTest {
|
||||||
signer.sign(jwt);
|
signer.sign(jwt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expected string based on the following strucutres, serialized exactly as follows and base64 encoded:
|
* Expected string based on the following structures, serialized exactly as follows and base64 encoded:
|
||||||
*
|
*
|
||||||
* header: {"typ":"JWT","alg":"HS256"}
|
* header: {"typ":"JWT","alg":"HS256"}
|
||||||
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
|
* claims: {"exp":1300819380,"iss":"joe","http://example.com/is_root":true}
|
||||||
|
@ -81,6 +96,40 @@ public class JwtTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGenerateRsaSignature() throws Exception {
|
||||||
|
|
||||||
|
// java.security.KeyStore ks = KeyStore.generateRsaKeyPair(keystore
|
||||||
|
// .getLocation().getFile().getPath(), "OpenID Connect Server",
|
||||||
|
// "twentyYears", KeyStore.PASSWORD, KeyStore.PASSWORD, 30, 365*20);
|
||||||
|
//
|
||||||
|
// keystore.setKeystore(ks);
|
||||||
|
|
||||||
|
Jwt jwt = new Jwt();
|
||||||
|
jwt.getHeader().setType("JWT");
|
||||||
|
jwt.getHeader().setAlgorithm("RS256");
|
||||||
|
jwt.getClaims().setExpiration(new Date(1300819380L * 1000L));
|
||||||
|
jwt.getClaims().setIssuer("joe");
|
||||||
|
jwt.getClaims().setClaim("http://example.com/is_root", Boolean.TRUE);
|
||||||
|
|
||||||
|
JwtSigner signer = new RsaSigner(RsaSigner.Algorithm.DEFAULT, keystore, "twentyYears");
|
||||||
|
((RsaSigner) signer).afterPropertiesSet();
|
||||||
|
|
||||||
|
signer.sign(jwt);
|
||||||
|
|
||||||
|
String signature = "TW0nOd_vr1rnV7yIS-lIV2-00V_zJMWxzOc3Z7k3gvMO2aIjIGjZ9nByZMI0iL5komMxYXPl_RCkbd9OKiPkk4iK5CDj7Mawbzu95LgEOOqdXO1f7-IqX9dIvJhVXXInLD3RsGvavyheIqNeFEVidLrJo30tBchB_niljEW7VeX8nSZfiCOdbOTW3hu0ycnon7wFpejb-cRP_S0iqGxCgbYXJzqPT192EHmRy_wmFxxIy9Lc84uqNkAZSIn1jVIeAemm22RoWbq0xLVLTRyiZoxJTUzac_VteiSPRNFlUQuOdxqNf0Hxqh_wVfX1mfXUzv0D8vHJVy6aIqTISmn-qg";
|
||||||
|
String expected = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjEzMDA4MTkzODAsImlzcyI6ImpvZSIsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.TW0nOd_vr1rnV7yIS-lIV2-00V_zJMWxzOc3Z7k3gvMO2aIjIGjZ9nByZMI0iL5komMxYXPl_RCkbd9OKiPkk4iK5CDj7Mawbzu95LgEOOqdXO1f7-IqX9dIvJhVXXInLD3RsGvavyheIqNeFEVidLrJo30tBchB_niljEW7VeX8nSZfiCOdbOTW3hu0ycnon7wFpejb-cRP_S0iqGxCgbYXJzqPT192EHmRy_wmFxxIy9Lc84uqNkAZSIn1jVIeAemm22RoWbq0xLVLTRyiZoxJTUzac_VteiSPRNFlUQuOdxqNf0Hxqh_wVfX1mfXUzv0D8vHJVy6aIqTISmn-qg";
|
||||||
|
|
||||||
|
String actual = jwt.toString();
|
||||||
|
|
||||||
|
assertThat(actual, equalTo(expected));
|
||||||
|
assertThat(jwt.getSignature(), equalTo(signature));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateHmacSignature() {
|
public void testValidateHmacSignature() {
|
||||||
// sign it
|
// sign it
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package org.mitre.jwt.signer.service.impl;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.Key;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
@SuppressWarnings("restriction") // I know...
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@ContextConfiguration(locations = {
|
||||||
|
"file:src/main/webapp/WEB-INF/spring/application-context.xml",
|
||||||
|
"classpath:test-context.xml" })
|
||||||
|
public class KeyStoreTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("testKeystore")
|
||||||
|
KeyStore keystore;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void storeKeyPair() throws GeneralSecurityException, IOException {
|
||||||
|
|
||||||
|
java.security.KeyStore ks = KeyStore.generateRsaKeyPair(keystore
|
||||||
|
.getLocation().getFile().getPath(), "OpenID Connect Server",
|
||||||
|
"test", KeyStore.PASSWORD, KeyStore.PASSWORD, 30, 30);
|
||||||
|
|
||||||
|
keystore.setKeystore(ks);
|
||||||
|
|
||||||
|
assertThat(ks, not(nullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readKey() throws GeneralSecurityException {
|
||||||
|
|
||||||
|
Key key = keystore.getKeystore().getKey("test",
|
||||||
|
KeyStore.PASSWORD.toCharArray());
|
||||||
|
|
||||||
|
System.out.println("-----BEGIN PRIVATE KEY-----");
|
||||||
|
System.out
|
||||||
|
.println(new sun.misc.BASE64Encoder().encode(key.getEncoded()));
|
||||||
|
System.out.println("-----END PRIVATE KEY-----");
|
||||||
|
|
||||||
|
assertThat(key, not(nullValue()));
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -1,7 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >
|
xmlns:jwt-signer="http://www.mitre.org/schema/openid-connect/jwt-signer"
|
||||||
|
xsi:schemaLocation=
|
||||||
|
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||||
|
http://www.mitre.org/schema/openid-connect/jwt-signer http://www.mitre.org/schema/openid-connect/jwt-signer/jwt-signer.xsd" >
|
||||||
|
|
||||||
<!-- Creates an in-memory database populated with test jdbc -->
|
<!-- Creates an in-memory database populated with test jdbc -->
|
||||||
<bean id="dataSource" class="org.mitre.jdbc.datasource.H2DataSourceFactory">
|
<bean id="dataSource" class="org.mitre.jdbc.datasource.H2DataSourceFactory">
|
||||||
|
@ -33,4 +36,7 @@
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<jwt-signer:keystore id="testKeystore" location="file:src/test/resources/keystore.jks" password="changeit" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
Loading…
Reference in New Issue