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.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.vipr.transform.TransformConstants;
import com.emc.vipr.transform.TransformException;
public class BasicEncryptionTransformFactory
extends
EncryptionTransformFactory<BasicEncryptionOutputTransform, BasicEncryptionInputTransform> {
Logger logger = LoggerFactory
.getLogger(BasicEncryptionTransformFactory.class);
public KeyPair masterEncryptionKey;
private String masterEncryptionKeyFingerprint;
private Map<String, KeyPair> masterDecryptionKeys;
public BasicEncryptionTransformFactory() throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException {
super();
masterDecryptionKeys = new HashMap<String, KeyPair>();
}
public BasicEncryptionTransformFactory(KeyPair masterEncryptionKey,
Set<KeyPair> masterDecryptionKeys) throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException {
this.masterDecryptionKeys = new HashMap<String, KeyPair>();
setMasterEncryptionKey(masterEncryptionKey);
if(masterDecryptionKeys != null) {
for(KeyPair kp : masterDecryptionKeys) {
addMasterDecryptionKey(kp);
}
}
}
public BasicEncryptionTransformFactory(KeyPair masterEncryptionKey,
Set<KeyPair> masterDecryptionKeys, Provider provider)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException {
super(TransformConstants.DEFAULT_ENCRYPTION_TRANSFORM,
TransformConstants.DEFAULT_ENCRYPTION_KEY_SIZE, provider);
this.masterDecryptionKeys = new HashMap<String, KeyPair>();
setMasterEncryptionKey(masterEncryptionKey);
if(masterDecryptionKeys != null) {
for(KeyPair kp : masterDecryptionKeys) {
addMasterDecryptionKey(kp);
}
}
}
public void setMasterEncryptionKey(KeyPair pair) {
if (!(pair.getPublic() instanceof RSAPublicKey)) {
throw new IllegalArgumentException(
"Only RSA KeyPairs are allowed, not "
+ pair.getPublic().getAlgorithm());
}
checkKeyLength(pair);
this.masterEncryptionKey = pair;
try {
this.masterEncryptionKeyFingerprint = KeyUtils
.getRsaPublicKeyFingerprint((RSAPublicKey) pair.getPublic(), provider);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Error adding master key", e);
}
addMasterDecryptionKey(pair);
}
public void addMasterDecryptionKey(KeyPair pair) {
try {
String fingerprint = KeyUtils
.getRsaPublicKeyFingerprint((RSAPublicKey) pair.getPublic(), provider);
masterDecryptionKeys.put(fingerprint, pair);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Error adding master key", e);
}
}
@Override
public Map<String, String> rekey(Map<String, String> metadata)
throws TransformException {
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 (!masterDecryptionKeys.containsKey(oldKeyId)) {
throw new TransformException("Master key with fingerprint "
+ oldKeyId + " not found");
}
KeyPair oldKey = masterDecryptionKeys.get(oldKeyId);
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,
oldKey.getPrivate());
// Re-encrypt key with the current master key
String newKey;
try {
newKey = KeyUtils.encryptKey(objectKey, provider,
masterEncryptionKey.getPublic());
} catch (GeneralSecurityException e) {
throw new TransformException("Could not re-encrypt 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) masterEncryptionKey.getPrivate(), provider);
newMetadata.put(TransformConstants.META_ENCRYPTION_META_SIG, signature);
return newMetadata;
}
@Override
public BasicEncryptionOutputTransform getOutputTransform(
OutputStream streamToEncodeTo, Map<String, String> metadataToEncode)
throws IOException {
return new BasicEncryptionOutputTransform(streamToEncodeTo,
metadataToEncode, masterEncryptionKeyFingerprint,
masterEncryptionKey, encryptionTransform, keySize, provider);
}
@Override
public BasicEncryptionOutputTransform getOutputTransform(
InputStream streamToEncode, Map<String, String> metadataToEncode)
throws IOException, TransformException {
return new BasicEncryptionOutputTransform(streamToEncode,
metadataToEncode, masterEncryptionKeyFingerprint,
masterEncryptionKey, 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.");
}
KeyPair masterKey = masterDecryptionKeys.get(masterKeyId);
if (masterKey == null) {
throw new TransformException(
"Could not decrypt object. No master key with ID "
+ masterKeyId + " found");
}
return new BasicEncryptionInputTransform(transformTuple[1],
streamToDecode, metadata, masterKey, provider);
}
/**
* Check for acceptable RSA key lengths. 1024-bit keys are not secure
* anymore and 512-bit keys are unacceptable. Newer JDKs have already
* removed support for the 512-bit keys and the 1024-bit keys may be removed
* in the future:
* http://mail.openjdk.java.net/pipermail/security-dev/2012-December/
* 006195.html
*
* @param pair
*/
private void checkKeyLength(KeyPair pair) {
// RSA key length is defined as the modulus of the public key
int keySize = ((RSAPublicKey) pair.getPublic()).getModulus()
.bitLength();
if (keySize < 1024) {
throw new IllegalArgumentException(
"The minimum RSA key size supported is 1024 bits. Your key is "
+ keySize + " bits");
} else if (keySize == 1024) {
logger.info("1024-bit RSA key detected. Support for 1024-bit RSA keys may soon be removed from the JDK. Please upgrade to a stronger key (e.g. 2048-bit).");
}
}
}