diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java index 709ba4ea7..fa0db1660 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java @@ -16,6 +16,11 @@ ******************************************************************************/ package org.mitre.jwt.encryption.service; +import java.util.Collection; +import java.util.Map; + +import com.nimbusds.jose.JWEAlgorithm; +import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jwt.EncryptedJWT; /** @@ -24,7 +29,26 @@ import com.nimbusds.jwt.EncryptedJWT; */ public interface JwtEncryptionAndDecryptionService { + /** + * Encrypts the JWT in place with the default encrypter. + * @param jwt + */ public void encryptJwt(EncryptedJWT jwt); + /** + * Decrypts the JWT in place with the default decrypter. + * @param jwt + */ public void decryptJwt(EncryptedJWT jwt); + + /** + * Get all public keys for this service, mapped by their Key ID + */ + public Map getAllPublicKeys(); + + /** + * Get the list of all encryption algorithms supported by this service. + * @return + */ + public Collection getAllEncryptionAlgsSupported(); } diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java index b52a9c4c3..f7d032287 100644 --- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java +++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java @@ -16,17 +16,30 @@ ******************************************************************************/ package org.mitre.jwt.encryption.service.impl; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import javax.annotation.PostConstruct; + +import org.mitre.jose.keystore.JWKSetKeyStore; import org.mitre.jwt.encryption.service.JwtEncryptionAndDecryptionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Strings; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWEDecrypter; import com.nimbusds.jose.JWEEncrypter; -import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.crypto.RSADecrypter; +import com.nimbusds.jose.crypto.RSAEncrypter; import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jwt.EncryptedJWT; /** @@ -36,22 +49,93 @@ import com.nimbusds.jwt.EncryptedJWT; public class DefaultJwtEncryptionAndDecryptionService implements JwtEncryptionAndDecryptionService { private static Logger logger = LoggerFactory.getLogger(DefaultJwtEncryptionAndDecryptionService.class); - - + // map of identifier to encrypter private Map encrypters = new HashMap(); // map of identifier to decrypter private Map decrypters = new HashMap(); - private String defaultEncryptionKeyId; + + private String defaultDecryptionKeyId; - private JWSAlgorithm defaultAlgorithm; + private JWEAlgorithm defaultAlgorithm; // map of identifier to key private Map keys = new HashMap(); + /** + * Build this service based on the keys given. All public keys will be used to make encrypters, + * all private keys will be used to make decrypters. + * + * @param keys + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws JOSEException + */ + public DefaultJwtEncryptionAndDecryptionService(Map keys) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { + this.keys = keys; + buildEncryptersAndDecrypters(); + } + + /** + * Build this service based on the given keystore. All keys must have a key + * id ({@code kid}) field in order to be used. + * + * @param keyStore + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws JOSEException + */ + public DefaultJwtEncryptionAndDecryptionService(JWKSetKeyStore keyStore) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { + + // convert all keys in the keystore to a map based on key id + for (JWK key : keyStore.getKeys()) { + if (!Strings.isNullOrEmpty(key.getKeyID())) { + this.keys.put(key.getKeyID(), key); + } else { + throw new IllegalArgumentException("Tried to load a key from a keystore without a 'kid' field: " + key); + } + } + buildEncryptersAndDecrypters(); + } + + + @PostConstruct + public void afterPropertiesSet() throws NoSuchAlgorithmException, InvalidKeySpecException{ + + if (keys == null) { + throw new IllegalArgumentException("Encryption and decryption service must have at least one key configured."); + } + + // TODO call build..() again? see default signer service. + } + + public String getDefaultEncryptionKeyId() { + return defaultEncryptionKeyId; + } + + public void setDefaultEncryptionKeyId(String defaultEncryptionKeyId) { + this.defaultEncryptionKeyId = defaultEncryptionKeyId; + } + + public String getDefaultDecryptionKeyId() { + return defaultDecryptionKeyId; + } + + public void setDefaultDecryptionKeyId(String defaultDecryptionKeyId) { + this.defaultDecryptionKeyId = defaultDecryptionKeyId; + } + + public JWEAlgorithm getDefaultAlgorithm() { + return defaultAlgorithm; + } + + public void setDefaultAlgorithm(JWEAlgorithm defaultAlgorithm) { + this.defaultAlgorithm = defaultAlgorithm; + } + /* (non-Javadoc) * @see org.mitre.jwt.encryption.service.JwtEncryptionAndDecryptionService#encryptJwt(com.nimbusds.jwt.EncryptedJWT) */ @@ -70,4 +154,71 @@ public class DefaultJwtEncryptionAndDecryptionService implements JwtEncryptionAn } + /** + * Builds all the encrypters and decrypters for this service based on the key map. + * @throws + * @throws InvalidKeySpecException + * @throws NoSuchAlgorithmException + * @throws JOSEException + */ + private void buildEncryptersAndDecrypters() throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { + + for (Map.Entry jwkEntry : keys.entrySet()) { + + String id = jwkEntry.getKey(); + JWK jwk = jwkEntry.getValue(); + + if (jwk instanceof RSAKey) { + // build RSA encrypters and decrypters + + RSAEncrypter encrypter = new RSAEncrypter(((RSAKey) jwk).toRSAPublicKey()); // there should always at least be the public key + encrypters.put(id, encrypter); + + if (jwk.isPrivate()) { // we can decrypt! + RSADecrypter decrypter = new RSADecrypter(((RSAKey) jwk).toRSAPrivateKey()); + decrypters.put(id, decrypter); + } else { + logger.warn("No private key for key #" + jwk.getKeyID()); + } + + // TODO: add support for EC keys + + } else { + logger.warn("Unknown key type: " + jwk); + } + + } + } + + @Override + public Map getAllPublicKeys() { + Map pubKeys = new HashMap(); + + // pull out all public keys + for (String keyId : keys.keySet()) { + JWK key = keys.get(keyId); + JWK pub = key.toPublicJWK(); + if (pub != null) { + pubKeys.put(keyId, pub); + } + } + + return pubKeys; + } + + @Override + public Collection getAllEncryptionAlgsSupported() { + Set algs = new HashSet(); + + for (JWEEncrypter enc : encrypters.values()) { + algs.addAll(enc.supportedAlgorithms()); + } + + for (JWEDecrypter dec : decrypters.values()) { + algs.addAll(dec.supportedAlgorithms()); + } + + return algs; + } + }