Blob Blame History Raw
commit 256db90fe28279879636597dec011ab8e2c073f8
Author: Jack Magne <jmagne@localhost.localdomain>
Date:   Tue Sep 22 10:26:07 2015 -0700

    KRA: key archival/recovery via cli - should honor encryption/decryption flags.
    
    Ticket # 1597
    
    Currently, KRA allows sites to opt for doing encryption/decryption instead of
    wrapping/unwrapping for key archival and recovery.
    
    The new cli code was later added without such support. We should honor the
    same flags when cli is called to do key archival and recovery.
    
    This feature was due to a specific customer request.
    Here is what is now supported:
    
    1. When the pki cli tool is used to recover a asymmetric private key,
    support is there to do so with encrypt / decrypt.
    
    2. The passphrase and generic data facility already uses
    encrypt / decrypt so nothing here was needed. Calling it out since
    this will possibly be a customer issue.
    
    3. While under the hood, it made sense to add this functionality to the
    Symmetric key archival and recovery operations.
    
    4. All tests in DRMTest.java worked successfully when the kra was
    configured to support this feature and configured to not observe this feature.
    
    What is missing:
    
    We have since added a method to do a server side key generation of an
    asymmetric key pair in the kra and also archive it there at the same time.
    In order to do encrypt / decrypt in this case we need to extract the key
    contents out of a key object that is used to generate this key. It proved
    problematic to extract said key. This should be ok since the customer only
    needs to recover an asymmetric key in their test cases. We could look into
    doing this later if a pressing need arises.
    
    (cherry picked from commit a5a50e95a691587e22335018538b4f578dfee6d1)

diff --git a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
index b244967..f12222b 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
@@ -25,6 +25,7 @@ import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.Random;
 
@@ -57,6 +58,7 @@ import org.mozilla.jss.util.Password;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.dbs.keydb.IKeyRecord;
 import com.netscape.certsrv.dbs.keydb.IKeyRepository;
 import com.netscape.certsrv.key.KeyRequestResource;
@@ -91,6 +93,7 @@ public class SecurityDataRecoveryService implements IService {
             "LOGGING_SIGNED_AUDIT_SECURITY_DATA_RECOVERY_REQUEST_PROCESSED_5";
     public static final String ATTR_SERIALNO = "serialNumber";
     public static final String ATTR_KEY_RECORD = "keyRecord";
+    private static boolean allowEncDecrypt_recovery = false;
 
     public SecurityDataRecoveryService(IKeyRecoveryAuthority kra) {
         mKRA = kra;
@@ -124,6 +127,15 @@ public class SecurityDataRecoveryService implements IService {
         byte iv_default[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
         byte iv_in[] = null;
 
+        IConfigStore config = null;
+
+        try {
+            config = CMS.getConfigStore();
+            allowEncDecrypt_recovery = config.getBoolean("kra.allowEncDecrypt.recovery", false);
+        } catch (Exception e) {
+            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+        }
+
         String requestor = request.getExtDataInString(IRequest.ATTR_REQUEST_OWNER);
         String auditSubjectID = requestor;
 
@@ -185,15 +197,24 @@ public class SecurityDataRecoveryService implements IService {
         PrivateKey privateKey = null;
 
         if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
-            symKey = recoverSymKey(keyRecord);
+            if (allowEncDecrypt_recovery == true) {
+                CMS.debug("Recover symmetric key by decrypting as per allowEncDecrypt_recovery: true.");
+                unwrappedSecData = recoverSecurityData(keyRecord);
+            } else {
+                symKey = recoverSymKey(keyRecord);
+            }
 
         } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
             unwrappedSecData = recoverSecurityData(keyRecord);
-
         } else if (dataType.equals(KeyRequestResource.ASYMMETRIC_KEY_TYPE)) {
             try {
-                privateKey = mStorageUnit.unwrap(keyRecord.getPrivateKeyData(),
-                        X509Key.parsePublicKey(new DerValue(keyRecord.getPublicKeyData())));
+                if (allowEncDecrypt_recovery == true) {
+                    CMS.debug("Recover asymmetric key by decrypting as per allowEncDecrypt_recovery: true.");
+                    unwrappedSecData = recoverSecurityData(keyRecord);
+                } else {
+                    privateKey = mStorageUnit.unwrap(keyRecord.getPrivateKeyData(),
+                            X509Key.parsePublicKey(new DerValue(keyRecord.getPublicKeyData())));
+                }
 
             } catch (IOException e) {
                 throw new EBaseException("Cannot fetch the private key from the database.", e);
@@ -223,17 +244,27 @@ public class SecurityDataRecoveryService implements IService {
                 passStr = null;
 
                 if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
-                    CMS.debug("SecurityDataRecoveryService: wrap stored symmetric key with transport passphrase");
-                    pbeWrappedData = createEncryptedContentInfo(ct, symKey, null, null,
-                            pass);
-                } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)){
+                    CMS.debug("SecurityDataRecoveryService: wrap or encrypt stored symmetric key with transport passphrase");
+                    if (allowEncDecrypt_recovery == true) {
+                        CMS.debug("SecurityDataRecoveryServic: allowEncDecyypt_recovery: true, symmetric key:  create blob with unwrapped key.");
+                        pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData, null, pass);
+                    } else {
+                        pbeWrappedData = createEncryptedContentInfo(ct, symKey, null, null,
+                                pass);
+                    }
+                } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
                     CMS.debug("SecurityDataRecoveryService: encrypt stored passphrase with transport passphrase");
                     pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData, null,
                             pass);
                 } else if (dataType.equals(KeyRequestResource.ASYMMETRIC_KEY_TYPE)) {
-                    CMS.debug("SecurityDataRecoveryService: wrap stored private key with transport passphrase");
-                    pbeWrappedData = createEncryptedContentInfo(ct, null, null, privateKey,
-                            pass);
+                    if (allowEncDecrypt_recovery == true) {
+                        CMS.debug("SecurityDataRecoveryService: allowEncDecyypt_recovery: true, asymmetric key:  create blob with unwrapped key.");
+                        pbeWrappedData = createEncryptedContentInfo(ct, null, unwrappedSecData, null, pass);
+                    } else {
+                        CMS.debug("SecurityDataRecoveryService: wrap stored private key with transport passphrase");
+                        pbeWrappedData = createEncryptedContentInfo(ct, null, null, privateKey,
+                                pass);
+                    }
                 }
 
                 params.put(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA, pbeWrappedData);
@@ -257,12 +288,27 @@ public class SecurityDataRecoveryService implements IService {
             CMS.debug("SecurityDataRecoveryService: secure retrieved data with session key");
 
             if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
-                CMS.debug("SecurityDataRecoveryService: wrap stored symmetric key with session key");
+                CMS.debug("SecurityDataRecoveryService: wrap or encrypt stored symmetric key with session key");
                 try {
-                    unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP);
-                    KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
-                    wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv));
-                    key_data = wrapper.wrap(symKey);
+                    if (allowEncDecrypt_recovery == true) {
+                        CMS.debug("SecurityDataRecoveryService: encrypt symmetric key with session key as per allowEncDecrypt_recovery: true.");
+                        unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.ENCRYPT);
+                        Cipher encryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD);
+                        if (encryptor != null) {
+                            encryptor.initEncrypt(unwrappedSess, new IVParameterSpec(iv));
+                            key_data = encryptor.doFinal(unwrappedSecData);
+                        } else {
+                            auditRecoveryRequestProcessed(auditSubjectID, ILogger.FAILURE, requestID,
+                                    serialno.toString(), "Failed to create cipher encrypting symmetric key");
+                            throw new IOException("Failed to create cipher encryping symmetric key");
+                        }
+
+                    } else {
+                        unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP);
+                        KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
+                        wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv));
+                        key_data = wrapper.wrap(symKey);
+                    }
 
                 } catch (Exception e) {
                     auditRecoveryRequestProcessed(auditSubjectID, ILogger.FAILURE, requestID, serialno.toString(),
@@ -292,12 +338,27 @@ public class SecurityDataRecoveryService implements IService {
                 }
 
             } else if (dataType.equals(KeyRequestResource.ASYMMETRIC_KEY_TYPE)) {
-                CMS.debug("SecurityDataRecoveryService: wrap stored private key with session key");
+                CMS.debug("SecurityDataRecoveryService: wrap or encrypt stored private key with session key");
                 try {
-                    unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP);
-                    KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
-                    wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv));
-                    key_data = wrapper.wrap(privateKey);
+                    if (allowEncDecrypt_recovery == true) {
+                        CMS.debug("SecurityDataRecoveryService: encrypt symmetric key with session key as per allowEncDecrypt_recovery: true.");
+                        unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.ENCRYPT);
+                        Cipher encryptor = ct.getCipherContext(EncryptionAlgorithm.DES3_CBC_PAD);
+                        if (encryptor != null) {
+                            encryptor.initEncrypt(unwrappedSess, new IVParameterSpec(iv));
+                            key_data = encryptor.doFinal(unwrappedSecData);
+                        } else {
+                            auditRecoveryRequestProcessed(auditSubjectID, ILogger.FAILURE, requestID,
+                                    serialno.toString(), "Failed to create cipher encrypting asymmetric key");
+                            throw new IOException("Failed to create cipher encrypting asymmetric key");
+                        }
+
+                    } else {
+                        unwrappedSess = mTransportUnit.unwrap_sym(wrappedSessKey, SymmetricKey.Usage.WRAP);
+                        KeyWrapper wrapper = ct.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD);
+                        wrapper.initWrap(unwrappedSess, new IVParameterSpec(iv));
+                        key_data = wrapper.wrap(privateKey);
+                    }
 
                 } catch (Exception e) {
                     auditRecoveryRequestProcessed(auditSubjectID, ILogger.FAILURE, requestID, serialno.toString(),
@@ -311,6 +372,10 @@ public class SecurityDataRecoveryService implements IService {
             params.put(IRequest.SECURITY_DATA_IV_STRING_OUT, ivStr);
         }
 
+        if(unwrappedSecData != null && unwrappedSecData.length > 0) {
+            Arrays.fill(unwrappedSecData, (byte)0);
+        }
+
         auditRecoveryRequestProcessed(auditSubjectID, ILogger.SUCCESS, requestID, serialno.toString(),
                 "None");
         request.setExtData(IRequest.RESULT, IRequest.RES_SUCCESS);
diff --git a/base/kra/src/com/netscape/kra/SecurityDataService.java b/base/kra/src/com/netscape/kra/SecurityDataService.java
index 25bb240..3a163e2 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataService.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataService.java
@@ -18,12 +18,14 @@
 package com.netscape.kra;
 
 import java.math.BigInteger;
+import java.util.Arrays;
 
 import org.dogtagpki.server.kra.rest.KeyRequestService;
 import org.mozilla.jss.crypto.SymmetricKey;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.dbs.keydb.IKeyRecord;
 import com.netscape.certsrv.dbs.keydb.IKeyRepository;
 import com.netscape.certsrv.key.KeyRequestResource;
@@ -53,6 +55,7 @@ public class SecurityDataService implements IService {
     private ITransportKeyUnit mTransportUnit = null;
     private IStorageKeyUnit mStorageUnit = null;
     private ILogger signedAuditLogger = CMS.getSignedAuditLogger();
+    private Boolean allowEncDecrypt_archival = false;
 
     private final static String LOGGING_SIGNED_AUDIT_SECURITY_DATA_ARCHIVAL_REQUEST_PROCESSED =
             "LOGGING_SIGNED_AUDIT_SECURITY_DATA_ARCHIVAL_REQUEST_PROCESSED_6";
@@ -100,6 +103,16 @@ public class SecurityDataService implements IService {
         CMS.debug("SecurityDataService.serviceRequest. Request id: " + id);
         CMS.debug("SecurityDataService.serviceRequest wrappedSecurityData: " + wrappedSecurityData);
 
+        IConfigStore config = null;
+
+        try {
+            config = CMS.getConfigStore();
+            allowEncDecrypt_archival = config.getBoolean("kra.allowEncDecrypt.archival", false);
+        } catch (Exception e) {
+            throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+        }
+
+
         String owner = request.getExtDataInString(IRequest.ATTR_REQUEST_OWNER);
         String auditSubjectID = owner;
 
@@ -146,16 +159,42 @@ public class SecurityDataService implements IService {
         byte[] securityData = null;
 
         String keyType = null;
+        byte [] tmp_unwrapped = null;
+        byte [] unwrapped = null;
         if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
             // Symmetric Key
             keyType = KeyRequestResource.SYMMETRIC_KEY_TYPE;
-            securitySymKey = mTransportUnit.unwrap_symmetric(
-                    wrappedSessionKey,
-                    algStr,
-                    sparams,
-                    secdata,
-                    KeyRequestService.SYMKEY_TYPES.get(algorithm),
-                    strength);
+
+            if (allowEncDecrypt_archival == true) {
+                tmp_unwrapped = mTransportUnit.decryptExternalPrivate(
+                        wrappedSessionKey,
+                        algStr,
+                        sparams,
+                        secdata);
+
+                if(tmp_unwrapped == null ) {
+                    throw new EBaseException("Can't decrypt symm key using allEncDecrypt_archival : true .");
+                }
+
+                /* making sure leading 0's are removed */
+                int first=0;
+                for (int j=0; (j< tmp_unwrapped.length) && (tmp_unwrapped[j]==0); j++) {
+                    first++;
+                }
+                unwrapped = Arrays.copyOfRange(tmp_unwrapped, first, tmp_unwrapped.length);
+                Arrays.fill(tmp_unwrapped, (byte)0);
+
+
+            } else {
+
+                securitySymKey = mTransportUnit.unwrap_symmetric(
+                        wrappedSessionKey,
+                        algStr,
+                        sparams,
+                        secdata,
+                        KeyRequestService.SYMKEY_TYPES.get(algorithm),
+                        strength);
+            }
 
         } else if (dataType.equals(KeyRequestResource.PASS_PHRASE_TYPE)) {
             keyType = KeyRequestResource.PASS_PHRASE_TYPE;
@@ -170,9 +209,13 @@ public class SecurityDataService implements IService {
         byte[] publicKey = null;
         byte privateSecurityData[] = null;
 
-        if (securitySymKey != null) {
+        if (securitySymKey != null && unwrapped == null) {
             privateSecurityData = mStorageUnit.wrap(securitySymKey);
-        } else if (securityData != null) {
+        } else if (unwrapped != null && allowEncDecrypt_archival == true) {
+            privateSecurityData = mStorageUnit.encryptInternalPrivate(unwrapped);
+            Arrays.fill(unwrapped, (byte)0);
+            CMS.debug("allowEncDecrypt_archival of symmetric key.");
+        }else if (securityData != null) {
             privateSecurityData = mStorageUnit.encryptInternalPrivate(securityData);
         } else { // We have no data.
             auditArchivalRequestProcessed(auditSubjectID, ILogger.FAILURE, request.getRequestId(),