moved to JPSK based key store
parent
e2ad4d2e8f
commit
5a04198eac
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.mitre.jose.keystore;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JWKSetKeyStore implements InitializingBean {
|
||||
|
||||
private JWKSet jwkSet;
|
||||
|
||||
private Resource location;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
if (jwkSet == null) {
|
||||
if (location != null) {
|
||||
|
||||
if (location.exists() && location.isReadable()) {
|
||||
|
||||
// read in the file from disk
|
||||
String s = CharStreams.toString(new InputStreamReader(location.getInputStream(), Charsets.UTF_8));
|
||||
|
||||
// parse it into a jwkSet object
|
||||
jwkSet = JWKSet.parse(s);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Key Set resource could not be read: " + location);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Key store must be initialized with at least one of a jwkSet or a location.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the jwkSet
|
||||
*/
|
||||
public JWKSet getJwkSet() {
|
||||
return jwkSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jwkSet the jwkSet to set
|
||||
*/
|
||||
public void setJwkSet(JWKSet jwkSet) {
|
||||
this.jwkSet = jwkSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the location
|
||||
*/
|
||||
public Resource getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param location the location to set
|
||||
*/
|
||||
public void setLocation(Resource location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass through to underlying JwK set and return its keys.
|
||||
* @return
|
||||
*/
|
||||
public List<JWK> getKeys() {
|
||||
return jwkSet.getKeys();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2012 The MITRE Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
package org.mitre.jwt.encryption.impl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Provider;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Creates and manages a JCE KeyStore
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class KeyStore implements InitializingBean {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(KeyStore.class);
|
||||
|
||||
public static final String TYPE = java.security.KeyStore.getDefaultType();
|
||||
public static final String PASSWORD = "changeit";
|
||||
|
||||
private String password;
|
||||
|
||||
private Resource location;
|
||||
|
||||
private java.security.KeyStore keystore;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
public KeyStore() {
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyStore constructor
|
||||
*
|
||||
* @param password
|
||||
* the password used to unlock the keystore
|
||||
* @param location
|
||||
* the location of the keystore
|
||||
*/
|
||||
public KeyStore(String password, Resource location) {
|
||||
setPassword(password);
|
||||
setLocation(location);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: a more specific exception perhaps? is an empty keystore even an exception?
|
||||
if (keystore.size() == 0) {
|
||||
throw new Exception("Keystore is empty; it has no entries");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 java.security.KeyStore getKeystore() {
|
||||
return keystore;
|
||||
}
|
||||
|
||||
public Resource getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public Provider getProvider() {
|
||||
return keystore.getProvider();
|
||||
}
|
||||
|
||||
public void setKeystore(java.security.KeyStore keystore) {
|
||||
this.keystore = keystore;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KeyStore [password=" + password + ", location=" + location
|
||||
+ ", keystore=" + keystore + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -17,23 +17,30 @@ package org.mitre.jwt.signer.service.impl;
|
|||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.mitre.jose.keystore.JWKSetKeyStore;
|
||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
||||
import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jose.JOSEException;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSSigner;
|
||||
import com.nimbusds.jose.JWSVerifier;
|
||||
import com.nimbusds.jose.crypto.MACSigner;
|
||||
import com.nimbusds.jose.crypto.MACVerifier;
|
||||
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||
import com.nimbusds.jose.crypto.RSASSAVerifier;
|
||||
import com.nimbusds.jose.jwk.ECKey;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
||||
public class DefaultJwtSigningAndValidationService implements JwtSigningAndValidationService, InitializingBean {
|
||||
|
@ -45,9 +52,11 @@ public class DefaultJwtSigningAndValidationService implements JwtSigningAndValid
|
|||
|
||||
private static Logger logger = LoggerFactory.getLogger(DefaultJwtSigningAndValidationService.class);
|
||||
|
||||
private String defaultSignerId;
|
||||
private String defaultSignerKeyId;
|
||||
|
||||
private JWSAlgorithm defaultAlgorithm;
|
||||
|
||||
private JWKSetKeyStore keyStore;
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
|
@ -64,38 +73,17 @@ public class DefaultJwtSigningAndValidationService implements JwtSigningAndValid
|
|||
}
|
||||
|
||||
/**
|
||||
* Load this signing and validation service from the given builders (which load keys from keystores)
|
||||
* @param builders
|
||||
* @return the defaultSignerKeyId
|
||||
*/
|
||||
public void setBuilders(Map<String, RSASSASignerVerifierBuilder> builders) {
|
||||
|
||||
for (Entry<String, RSASSASignerVerifierBuilder> e : builders.entrySet()) {
|
||||
|
||||
JWSSigner signer = e.getValue().buildSigner();
|
||||
signers.put(e.getKey(), signer);
|
||||
if (e.getValue().isDefault()) {
|
||||
defaultSignerId = e.getKey();
|
||||
}
|
||||
|
||||
JWSVerifier verifier = e.getValue().buildVerifier();
|
||||
verifiers.put(e.getKey(), verifier);
|
||||
|
||||
}
|
||||
|
||||
public String getDefaultSignerKeyId() {
|
||||
return defaultSignerKeyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the defaultSignerId
|
||||
* @param defaultSignerKeyId the defaultSignerKeyId to set
|
||||
*/
|
||||
public String getDefaultSignerId() {
|
||||
return defaultSignerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param defaultSignerId the defaultSignerId to set
|
||||
*/
|
||||
public void setDefaultSignerId(String defaultSignerId) {
|
||||
this.defaultSignerId = defaultSignerId;
|
||||
public void setDefaultSignerKeyId(String defaultSignerId) {
|
||||
this.defaultSignerKeyId = defaultSignerId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,6 +105,20 @@ public class DefaultJwtSigningAndValidationService implements JwtSigningAndValid
|
|||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return the keyStore
|
||||
*/
|
||||
public JWKSetKeyStore getKeyStore() {
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keyStore the keyStore to set
|
||||
*/
|
||||
public void setKeyStore(JWKSetKeyStore keyStore) {
|
||||
this.keyStore = keyStore;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
@ -124,7 +126,47 @@ public class DefaultJwtSigningAndValidationService implements JwtSigningAndValid
|
|||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet(){
|
||||
public void afterPropertiesSet() throws NoSuchAlgorithmException, InvalidKeySpecException{
|
||||
|
||||
if (keyStore != null) {
|
||||
// if we have a keystore, load it up into our signers
|
||||
|
||||
List<JWK> keys = keyStore.getKeys();
|
||||
for (JWK jwk : keys) {
|
||||
|
||||
if (!Strings.isNullOrEmpty(jwk.getKeyID())) {
|
||||
|
||||
if (jwk instanceof RSAKey) {
|
||||
// build RSA signers & verifiers
|
||||
RSASSASigner signer = new RSASSASigner(((RSAKey) jwk).toRSAPrivateKey());
|
||||
RSASSAVerifier verifier = new RSASSAVerifier(((RSAKey) jwk).toRSAPublicKey());
|
||||
|
||||
signers.put(jwk.getKeyID(), signer);
|
||||
verifiers.put(jwk.getKeyID(), verifier);
|
||||
|
||||
} else if (jwk instanceof ECKey) {
|
||||
// build EC signers & verifiers
|
||||
|
||||
// TODO: add support for EC keys
|
||||
logger.warn("EC Keys are not yet supported.");
|
||||
|
||||
} else if (jwk instanceof OctetSequenceKey) {
|
||||
// build HMAC signers & verifiers
|
||||
MACSigner signer = new MACSigner(((OctetSequenceKey) jwk).toByteArray());
|
||||
MACVerifier verifier = new MACVerifier(((OctetSequenceKey) jwk).toByteArray());
|
||||
|
||||
signers.put(jwk.getKeyID(), signer);
|
||||
verifiers.put(jwk.getKeyID(), verifier);
|
||||
} else {
|
||||
logger.warn("Unknown key type: " + jwk);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Found a key with no KeyId: " + jwk);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.info("DefaultJwtSigningAndValidationService is ready: " + this.toString());
|
||||
}
|
||||
|
||||
|
@ -133,11 +175,11 @@ public class DefaultJwtSigningAndValidationService implements JwtSigningAndValid
|
|||
*/
|
||||
@Override
|
||||
public void signJwt(SignedJWT jwt) {
|
||||
if (getDefaultSignerId() == null) {
|
||||
if (getDefaultSignerKeyId() == null) {
|
||||
throw new IllegalStateException("Tried to call default signing with no default signer ID set");
|
||||
}
|
||||
|
||||
JWSSigner signer = signers.get(getDefaultSignerId());
|
||||
JWSSigner signer = signers.get(getDefaultSignerKeyId());
|
||||
|
||||
try {
|
||||
jwt.sign(signer);
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import org.mitre.jwt.encryption.impl.KeyStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||
import com.nimbusds.jose.crypto.RSASSAVerifier;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class RSASSASignerVerifierBuilder {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(RSASSASignerVerifierBuilder.class);
|
||||
|
||||
private String alias;
|
||||
private String password;
|
||||
private KeyStore keystore;
|
||||
private boolean defaultSigner = false;
|
||||
|
||||
/**
|
||||
* @return the alias
|
||||
*/
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
/**
|
||||
* @param alias the alias to set
|
||||
*/
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
/**
|
||||
* @return the password
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
/**
|
||||
* @param password the password to set
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
/**
|
||||
* @return the keystore
|
||||
*/
|
||||
public KeyStore getKeystore() {
|
||||
return keystore;
|
||||
}
|
||||
/**
|
||||
* @param keystore the keystore to set
|
||||
*/
|
||||
public void setKeystore(KeyStore keystore) {
|
||||
this.keystore = keystore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the defaultSigner
|
||||
*/
|
||||
public boolean isDefault() {
|
||||
return defaultSigner;
|
||||
}
|
||||
/**
|
||||
* @param defaultSigner the defaultSigner to set
|
||||
*/
|
||||
public void setDefault(boolean defaultSigner) {
|
||||
this.defaultSigner = defaultSigner;
|
||||
}
|
||||
/**
|
||||
* Build the signer as configured from the given keystore, null if it can't be built for some reason
|
||||
* @return
|
||||
*/
|
||||
public RSASSASigner buildSigner() {
|
||||
|
||||
try {
|
||||
KeyPair keyPair = keystore.getKeyPairForAlias(alias, password);
|
||||
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
|
||||
if (privateKey instanceof RSAPrivateKey) {
|
||||
RSASSASigner signer = new RSASSASigner((RSAPrivateKey) privateKey);
|
||||
return signer;
|
||||
} else {
|
||||
log.warn("Couldn't build signer, referenced key is not RSA");
|
||||
return null;
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
log.warn("Couldn't buld signer:", e);
|
||||
|
||||
}
|
||||
|
||||
log.warn("Couldn't build signer");
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the signer as configured from the given keystore, null if it can't be built for some reason
|
||||
* @return
|
||||
*/
|
||||
public RSASSAVerifier buildVerifier() {
|
||||
|
||||
try {
|
||||
KeyPair keyPair = keystore.getKeyPairForAlias(alias, password);
|
||||
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
|
||||
if (publicKey instanceof RSAPublicKey) {
|
||||
RSASSAVerifier signer = new RSASSAVerifier((RSAPublicKey) publicKey);
|
||||
return signer;
|
||||
} else {
|
||||
log.warn("Couldn't build verifier, referenced key is not RSA");
|
||||
return null;
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
log.warn("Couldn't buld verifier:", e);
|
||||
|
||||
}
|
||||
|
||||
log.warn("Couldn't build verifier");
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"keys":
|
||||
[
|
||||
{
|
||||
"alg": "RS256",
|
||||
"d": "RkRPP2wJQQWOTTjgRnt4WLEl2rpo8Vpg586NhrE8QbJfu0XfrrTL6_pz0kCyqcFcy0Aq0QO5-Rn56M7aZFE5cqo8xq4pPM32IfvlKTuxQxW_0I9fod-dy9GMnhVCO9WKhXQ23H6vKYdq0gsI9ov_S1jIN7JPwdyzzLpAGXf12I0",
|
||||
"e": "AQAB",
|
||||
"n": "23zs5r8PQKpsKeoUd2Bjz3TJkUljWqMD8X98SaIb1LE7dCQzi9jwO58FGL0ieY1Dfnr9-g1iiY8sNzV-byawK98W9yFiopaghfoKtxXgUD8pi0fLPeWmAkntjn28Z_WZvvA265ELbBhphPXEJcFhdzUfgESHVuqFMEqp1pB-CP0",
|
||||
"kty": "RSA",
|
||||
"kid": "rsa1"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -3,25 +3,14 @@
|
|||
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.xsd">
|
||||
|
||||
<bean id="defaultKeystore" class="org.mitre.jwt.encryption.impl.KeyStore">
|
||||
<constructor-arg name="location" value="classpath:keystore.jks" />
|
||||
<constructor-arg name="password" value="changeit" />
|
||||
<bean id="defaultKeyStore" class="org.mitre.jose.keystore.JWKSetKeyStore">
|
||||
<property name="location" value="classpath:keystore.jwks" />
|
||||
</bean>
|
||||
|
||||
<bean id="defaultsignerService"
|
||||
class="org.mitre.jwt.signer.service.impl.DefaultJwtSigningAndValidationService">
|
||||
<property name="builders">
|
||||
<map>
|
||||
<entry key="rsa1">
|
||||
<bean id="rsaSignerBuilder" class="org.mitre.jwt.signer.service.impl.RSASSASignerVerifierBuilder">
|
||||
<property name="keystore" ref="defaultKeystore" />
|
||||
<property name="alias" value="rsa" />
|
||||
<property name="password" value="changeit" />
|
||||
<property name="default" value="true" />
|
||||
</bean>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="keyStore" ref="defaultKeyStore" />
|
||||
<property name="defaultSignerKeyId" value="rsa1" />
|
||||
<property name="defaultSigningAlgorithmName" value="RS256" />
|
||||
</bean>
|
||||
|
||||
|
|
Loading…
Reference in New Issue