package com.emc.vipr.transform.encryption; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.vipr.transform.TransformConstants; import com.emc.vipr.transform.TransformException; public class KeyStoreEncryptionFactory extends EncryptionTransformFactory<BasicEncryptionOutputTransform, BasicEncryptionInputTransform> { private static final Logger logger = LoggerFactory.getLogger(KeyStoreEncryptionFactory.class); private KeyStore keyStore; private String masterEncryptionKeyAlias; private String masterEncryptionKeyFingerprint; private char[] masterKeyPassword; private Map<String, String> idToAliasMap; public KeyStoreEncryptionFactory(KeyStore keyStore, String masterEncryptionKeyAlias, char[] keyStorePassword) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, TransformException { this(keyStore, masterEncryptionKeyAlias, keyStorePassword, null); } public KeyStoreEncryptionFactory(KeyStore keyStore, String masterEncryptionKeyAlias, char[] masterKeyPassword, Provider provider) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, TransformException { super(); this.keyStore = keyStore; this.masterEncryptionKeyAlias = masterEncryptionKeyAlias; this.masterKeyPassword = masterKeyPassword; this.idToAliasMap = new HashMap<String, String>(); this.provider = provider; // Make sure the master encryption key alias exists. try { if(!keyStore.containsAlias(masterEncryptionKeyAlias)) { throw new InvalidKeyException("No certificate found in keystore for alias " + masterEncryptionKeyAlias); } } catch (KeyStoreException e) { throw new TransformException("Could not access KeyStore", e); } // Index all the certificate fingerprints try { for(Enumeration<String> aliases = keyStore.aliases(); aliases.hasMoreElements();) { String alias = aliases.nextElement(); String fingerprint = getFingerprint(alias); idToAliasMap.put(fingerprint, alias); if(alias.equals(masterEncryptionKeyAlias)) { masterEncryptionKeyFingerprint = fingerprint; } } } catch(KeyStoreException e) { throw new TransformException("Could not init factory from KeyStore", e); } } private String getFingerprint(String alias) throws KeyStoreException, NoSuchAlgorithmException { Certificate cert = keyStore.getCertificate(alias); if(cert instanceof X509Certificate) { // Get SKI byte[] ski = ((X509Certificate)cert).getExtensionValue("2.5.29.14"); if(ski == null) { logger.debug("Certificate does not have SKI. Computing fingerprint."); return KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) cert.getPublicKey(), provider); } String fingerprint = KeyUtils.toHexPadded(KeyUtils.extractSubjectKeyIdentifier(ski)); logger.debug("Alias %s Subject Key Identifier: %s", alias, fingerprint); return fingerprint; } else { // Compute the fingerprint return KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) cert.getPublicKey(), provider); } } @Override public Map<String, String> rekey(Map<String, String> metadata) throws TransformException, DoesNotNeedRekeyException{ String oldKeyId = metadata .get(TransformConstants.META_ENCRYPTION_KEY_ID); if (oldKeyId == null) { throw new TransformException( "Metadata does not contain a master key ID"); } if (oldKeyId.equals(masterEncryptionKeyFingerprint)) { // This object is already using the current key. logger.info("Object is already using the current master key"); throw new DoesNotNeedRekeyException( "Object is already using the current master key"); } // Make sure we have the old key if (!idToAliasMap.containsKey(oldKeyId)) { throw new TransformException("Master key with fingerprint " + oldKeyId + " not found"); } String oldAlias = idToAliasMap.get(oldKeyId); KeyPair oldMasterKey = getKeyPair(oldAlias); String encodedKey = metadata.get(TransformConstants.META_ENCRYPTION_OBJECT_KEY); if(encodedKey == null) { throw new TransformException("Encrypted object key not found"); } String algorithm = getEncryptionAlgorithm(); SecretKey objectKey = KeyUtils.decryptKey(encodedKey, algorithm, provider, oldMasterKey.getPrivate()); // Re-encrypt key with the current master key KeyPair newMasterKey = getKeyPair(masterEncryptionKeyAlias); String newKey; try { newKey = KeyUtils.encryptKey(objectKey, provider, newMasterKey.getPublic()); } catch (GeneralSecurityException e) { throw new TransformException("Error encrypting key: " + e, e); } Map<String, String> newMetadata = new HashMap<String, String>(); newMetadata.putAll(metadata); newMetadata.remove(TransformConstants.META_ENCRYPTION_META_SIG); newMetadata.put(TransformConstants.META_ENCRYPTION_OBJECT_KEY, newKey); newMetadata.put(TransformConstants.META_ENCRYPTION_KEY_ID, masterEncryptionKeyFingerprint); // Re-sign String signature = KeyUtils.signMetadata(newMetadata, (RSAPrivateKey) newMasterKey.getPrivate(), provider); newMetadata.put(TransformConstants.META_ENCRYPTION_META_SIG, signature); return newMetadata; } private KeyPair getKeyPair(String alias) throws TransformException { Certificate keyCert; PrivateKey privateKey; try { keyCert = keyStore.getCertificate(alias); privateKey = (PrivateKey) keyStore.getKey(alias, masterKeyPassword); if(keyCert == null) { throw new TransformException("Certificate for alias " + masterEncryptionKeyAlias + " not found"); } if(privateKey == null) { throw new TransformException("Private key for alias " + masterEncryptionKeyAlias + " not found"); } } catch (KeyStoreException e) { throw new TransformException("Could not access keystore", e); } catch(UnrecoverableKeyException e) { throw new TransformException("Error loading private key from keystore", e); } catch(NoSuchAlgorithmException e) { throw new TransformException("Error loading private key from keystore", e); } return new KeyPair(keyCert.getPublicKey(), privateKey); } @Override public BasicEncryptionOutputTransform getOutputTransform( OutputStream streamToEncodeTo, Map<String, String> metadataToEncode) throws IOException, TransformException { // Load the key KeyPair asymmetricKey = getKeyPair(masterEncryptionKeyAlias); return new BasicEncryptionOutputTransform(streamToEncodeTo, metadataToEncode, masterEncryptionKeyFingerprint, asymmetricKey, encryptionTransform, keySize, provider); } @Override public BasicEncryptionOutputTransform getOutputTransform( InputStream streamToEncode, Map<String, String> metadataToEncode) throws IOException, TransformException { // Load the key KeyPair asymmetricKey = getKeyPair(masterEncryptionKeyAlias); return new BasicEncryptionOutputTransform(streamToEncode, metadataToEncode, masterEncryptionKeyFingerprint, asymmetricKey, encryptionTransform, keySize, provider); } @Override public BasicEncryptionInputTransform getInputTransform( String transformConfig, InputStream streamToDecode, Map<String, String> metadata) throws IOException, TransformException { String[] transformTuple = splitTransformConfig(transformConfig); if (transformTuple.length != 2) { throw new TransformException("Invalid transform configuration: " + transformConfig); } if (!TransformConstants.ENCRYPTION_CLASS.equals(transformTuple[0])) { throw new TransformException("Unsupported transform class: " + transformTuple[0]); } // Find master key String masterKeyId = metadata .get(TransformConstants.META_ENCRYPTION_KEY_ID); if (masterKeyId == null) { throw new TransformException( "Could not decrypt object. No master key ID set on object."); } String masterKeyAlias = idToAliasMap.get(masterKeyId); if(masterKeyAlias == null) { throw new TransformException("Could not find master key for ID " + masterKeyId); } KeyPair asymmetricKey = getKeyPair(masterKeyAlias); return new BasicEncryptionInputTransform(transformTuple[1], streamToDecode, metadata, asymmetricKey, provider); } public String getMasterEncryptionKeyAlias() { return masterEncryptionKeyAlias; } public void setMasterEncryptionKeyAlias(String alias) throws TransformException { try { // Make sure it exists if(!keyStore.containsAlias(alias)) { throw new TransformException("Certificate with alias " + alias + " not found in keystore"); } // Get the fingerprint too String fingerprint = getFingerprint(alias); masterEncryptionKeyFingerprint = fingerprint; masterEncryptionKeyAlias = alias; } catch (KeyStoreException e) { throw new TransformException("Could not access keystore", e); } catch (NoSuchAlgorithmException e) { throw new TransformException("Could not load certificate for alias " + alias ); } } }