>From 98ddde7136cb439ee997e2c52e0caa8317f86b42 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Mon, 24 Feb 2014 15:31:12 -0500
Subject: [PATCH 205/205] Add ability to archive without sending
 pkiArchiveOptions object.

With this patch, you can now either send a pkiArchiveOptions object
or the exploded parameters.  This reduces the processing required on
the client side.
---
 base/common/python/pki/__init__.py                 |  14 +-
 base/common/python/pki/key.py                      | 174 +++++++++++++--------
 .../netscape/certsrv/key/KeyArchivalRequest.java   |  70 ++++++++-
 .../src/com/netscape/certsrv/key/KeyClient.java    |  23 ++-
 .../netscape/certsrv/profile/IEnrollProfile.java   |  20 +++
 base/kra/functional/drmtest.py                     |  15 +-
 .../src/com/netscape/cms/servlet/test/DRMTest.java |  12 +-
 .../src/com/netscape/kra/SecurityDataService.java  |  81 +++++++---
 .../netscape/cms/servlet/key/KeyRequestDAO.java    |  18 ++-
 .../cms/servlet/request/KeyRequestService.java     |  17 +-
 10 files changed, 337 insertions(+), 107 deletions(-)

diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index 7e4e2771c72ccd5a329ed44ea237ac0fe592dadd..a747ddb2deae1ccaaf7655d60c6c1cf3e2af86ea 100644
--- a/base/common/python/pki/__init__.py
+++ b/base/common/python/pki/__init__.py
@@ -94,6 +94,7 @@ def implementation_version():
 
     raise Exception('Missing implementation version.')
 
+#pylint: disable-msg=R0903
 class Attribute(object):
     '''
     Class representing a key/value pair.
@@ -106,6 +107,7 @@ class Attribute(object):
         self.name = name
         self.value = value
 
+#pylint: disable-msg=R0903
 class AttributeList(object):
     '''
     Class representing a list of attributes.
@@ -113,6 +115,7 @@ class AttributeList(object):
     This class is needed because of a JavaMapper used in the REST API.
     '''
 
+    # pylint: disable-msg=C0103
     def __init__(self):
         ''' Constructor '''
         self.Attribute = []
@@ -123,6 +126,7 @@ class ResourceMessage(object):
     It is essentially a list of attributes.
     '''
 
+    # pylint: disable-msg=C0103
     def __init__(self, class_name):
         ''' Constructor '''
         self.Attributes = AttributeList()
@@ -246,24 +250,24 @@ class PropertyFile(object):
         self.lines = []
 
     def read(self):
-        ''' Read from propert file '''
+        ''' Read from property file '''
         self.lines = []
 
         if not os.path.exists(self.filename):
             return
 
         # read all lines and preserve the original order
-        with open(self.filename, 'r') as f:
-            for line in f:
+        with open(self.filename, 'r') as f_in:
+            for line in f_in:
                 line = line.strip('\n')
                 self.lines.append(line)
 
     def write(self):
         ''' Write to property file '''
         # write all lines in the original order
-        with open(self.filename, 'w') as f:
+        with open(self.filename, 'w') as f_out:
             for line in self.lines:
-                f.write(line + '\n')
+                f_out.write(line + '\n')
 
     def show(self):
         ''' Show contents of property file.'''
diff --git a/base/common/python/pki/key.py b/base/common/python/pki/key.py
index b4158fed6a750cc00fdd01237629caf0ee40e801..de9fcc959627486404b4564ae408e7ca51a2bbc2 100644
--- a/base/common/python/pki/key.py
+++ b/base/common/python/pki/key.py
@@ -30,6 +30,7 @@ import pki
 import types
 import urllib
 
+#pylint: disable-msg=R0903
 class KeyId(object):
     '''
     Class representing a key ID
@@ -39,6 +40,7 @@ class KeyId(object):
         self.value = key_id
 
 #should be moved to request.py
+#pylint: disable-msg=R0903
 class RequestId(object):
     '''
     Class representing a Request ID
@@ -47,12 +49,14 @@ class RequestId(object):
         ''' Constructor'''
         self.value = req_id
 
+#pylint: disable-msg=R0903
 class KeyData(object):
     '''
     This is the object that contains the wrapped secret
     when that secret is retrieved.
     '''
 
+    # pylint: disable-msg=C0103
     def __init__(self):
         ''' Constructor '''
         self.algorithm = None
@@ -75,6 +79,7 @@ class KeyInfo(object):
     contain the secret itself.
     '''
 
+    # pylint: disable-msg=C0103
     def __init__(self):
         ''' Constructor '''
         self.clientKeyID = None
@@ -99,7 +104,7 @@ class KeyInfo(object):
             return str(self.keyURL)[indx:]
         return None
 
-
+#pylint: disable-msg=R0903
 class KeyInfoCollection(object):
     '''
     This class represents data returned when searching the DRM archived
@@ -129,6 +134,7 @@ class KeyRequestInfo(object):
     key generation etc.) in the DRM.
     '''
 
+    # pylint: disable-msg=C0103
     def __init__(self):
         ''' Constructor '''
         self.requestURL = None
@@ -158,6 +164,7 @@ class KeyRequestInfo(object):
             return str(self.keyURL)[indx:]
         return None
 
+#pylint: disable-msg=R0903
 class KeyRequestInfoCollection(object):
     '''
     This class represents the data returned when searching the key
@@ -190,6 +197,7 @@ class KeyRequestResponse(object):
     which contains the wrapped secret (if that operation is supported).
     '''
 
+    # pylint: disable-msg=C0103
     def __init__(self):
         ''' Constructor '''
         self.requestInfo = None
@@ -221,15 +229,32 @@ class KeyArchivalRequest(pki.ResourceMessage):
     '''
 
     def __init__(self, client_key_id=None, data_type=None, wrapped_private_data=None,
+                 trans_wrapped_session_key=None, pki_archive_options=None,
+                 algorithm_oid = None, symkey_params = None,
                  key_algorithm=None, key_size=None):
         ''' Constructor '''
         pki.ResourceMessage.__init__(self,
                                  "com.netscape.certsrv.key.KeyArchivalRequest")
         self.add_attribute("clientKeyID", client_key_id)
         self.add_attribute("dataType", data_type)
-        self.add_attribute("wrappedPrivateData", wrapped_private_data)
-        self.add_attribute("keyAlgorithm", key_algorithm)
-        self.add_attribute("keySize", key_size)
+
+        if wrapped_private_data is not None:
+            self.add_attribute("wrappedPrivateData", wrapped_private_data)
+        if trans_wrapped_session_key is not None:
+            self.add_attribute("transWrappedSessionKey", trans_wrapped_session_key)
+        if algorithm_oid is not None:
+            self.add_attribute("algorithmOID", algorithm_oid)
+        if symkey_params is not None:
+            self.add_attribute("symmetricAlgorithmParams", symkey_params)
+
+        if pki_archive_options is not None:
+            self.add_attribute("pkiArchiveOptions", pki_archive_options)
+
+        if key_algorithm is not None:
+            self.add_attribute("keyAlgorithm", key_algorithm)
+
+        if key_size is not None:
+            self.add_attribute("keySize", key_size)
 
 class KeyRecoveryRequest(pki.ResourceMessage):
     '''
@@ -292,13 +317,20 @@ class KeyClient(object):
     KEY_STATUS_ACTIVE = "active"
     KEY_STATUS_INACTIVE = "inactive"
 
+    DES_ALGORITHM = "DES"
+    DESEDE_ALGORITHM = "DESede"
+    DES3_ALGORITHM = "DES3"
+    RC2_ALGORITHM = "RC2"
+    RC4_ALGORITHM = "RC4"
+    AES_ALGORITHM = "AES"
+
     def __init__(self, connection, crypto, transport_cert_nick=None):
         ''' Constructor '''
         self.connection = connection
         self.headers = {'Content-type': 'application/json',
                         'Accept': 'application/json'}
-        self.keyURL = '/rest/agent/keys'
-        self.keyRequestsURL = '/rest/agent/keyrequests'
+        self.key_url = '/rest/agent/keys'
+        self.key_requests_url = '/rest/agent/keyrequests'
         self.crypto = crypto
 
         if transport_cert_nick is not None:
@@ -324,7 +356,7 @@ class KeyClient(object):
         query_params = {'clientKeyID':client_key_id, 'status':status,
                         'maxResults':max_results, 'maxTime':max_time,
                         'start':start, 'size':size}
-        response = self.connection.get(self.keyURL, self.headers, params=query_params)
+        response = self.connection.get(self.key_url, self.headers, params=query_params)
         return KeyInfoCollection.from_json(response.json())
 
     @pki.handle_exceptions()
@@ -338,7 +370,7 @@ class KeyClient(object):
         query_params = {'requestState':request_state, 'requestType':request_type,
                         'clientKeyID':client_key_id, 'start':start, 'pageSize':page_size,
                         'maxResults':max_results, 'maxTime':max_time}
-        response = self.connection.get(self.keyRequestsURL, self.headers,
+        response = self.connection.get(self.key_requests_url, self.headers,
                                 params=query_params)
         return KeyRequestInfoCollection.from_json(response.json())
 
@@ -348,7 +380,7 @@ class KeyClient(object):
         if request_id is None:
             raise ValueError("request_id must be specified")
 
-        url = self.keyRequestsURL + '/' + request_id
+        url = self.key_requests_url + '/' + request_id
         response = self.connection.get(url, self.headers)
         return KeyRequestInfo.from_json(response.json())
 
@@ -358,7 +390,7 @@ class KeyClient(object):
         if key_id is None:
             raise ValueError("key_id must be specified")
 
-        url = self.keyURL + '/' + key_id
+        url = self.key_url + '/' + key_id
         response = self.connection.get(url, headers=self.headers)
         return KeyInfo.from_json(response.json())
 
@@ -368,7 +400,7 @@ class KeyClient(object):
         if client_key_id is None:
             raise ValueError("client_key_id must be specified")
 
-        url = self.keyURL + '/active/' + urllib.quote_plus(client_key_id)
+        url = self.key_url + '/active/' + urllib.quote_plus(client_key_id)
         response = self.connection.get(url, headers=self.headers)
         return KeyInfo.from_json(response.json())
 
@@ -378,7 +410,7 @@ class KeyClient(object):
         if (key_id is None) or (status is None):
             raise ValueError("key_id and status must be specified")
 
-        url = self.keyURL + '/' + key_id
+        url = self.key_url + '/' + key_id
         params = {'status':status}
         self.connection.post(url, None, headers=self.headers, params=params)
 
@@ -388,7 +420,7 @@ class KeyClient(object):
         if request_id is None:
             raise ValueError("request_id must be specified")
 
-        url = self.keyRequestsURL + '/' + request_id + '/approve'
+        url = self.key_requests_url + '/' + request_id + '/approve'
         self.connection.post(url, self.headers)
 
     @pki.handle_exceptions()
@@ -397,7 +429,7 @@ class KeyClient(object):
         if request_id is None:
             raise ValueError("request_id must be specified")
 
-        url = self.keyRequestsURL + '/' + request_id + '/reject'
+        url = self.key_requests_url + '/' + request_id + '/reject'
         self.connection.post(url, self.headers)
 
     @pki.handle_exceptions()
@@ -406,39 +438,9 @@ class KeyClient(object):
         if request_id is None:
             raise ValueError("request_id must be specified")
 
-        url = self.keyRequestsURL + '/' + request_id + '/cancel'
+        url = self.key_requests_url + '/' + request_id + '/cancel'
         self.connection.post(url, self.headers)
 
-    def generate_pki_archive_options(self, trans_wrapped_session_key, session_wrapped_secret):
-        ''' Return a PKIArchiveOptions structure for archiving a secret
-
-            Takes in a session key wrapped by the DRM transport certificate,
-            and a secret wrapped with the session key and creates a PKIArchiveOptions
-            structure to be used when archiving a secret
-        '''
-        pass
-
-    def generate_archive_options(self, secret):
-        ''' Return a PKIArchiveOptions structure for archiving a secret.
-
-            This method uses NSS calls to do the following:
-            1) generate a session key
-            2) wrap the session key with the transport key
-            3) wrap the secret with the session key
-            4) create the PKIArchiveOptions structure using the results of
-               (2) and (3)
-
-            This method expects initialize_nss() to have been called previously.
-        '''
-        if secret is None:
-            raise ValueError("secret must be specified")
-
-        session_key = self.crypto.generate_session_key()
-        trans_wrapped_session_key = self.crypto.asymmetric_wrap(session_key, self.transport_cert)
-        wrapped_secret = self.crypto.symmetric_wrap(secret, session_key)
-
-        return self.generate_pki_archive_options(trans_wrapped_session_key, wrapped_secret)
-
     @pki.handle_exceptions()
     def create_request(self, request):
         ''' Submit an archival, recovery or key generation request
@@ -452,7 +454,7 @@ class KeyClient(object):
         if request is None:
             raise ValueError("request must be specified")
 
-        url = self.keyRequestsURL
+        url = self.key_requests_url
         key_request = json.dumps(request, cls=encoder.CustomTypeEncoder, sort_keys=True)
         response = self.connection.post(url, key_request, self.headers)
         return KeyRequestResponse.from_json(response.json())
@@ -489,7 +491,9 @@ class KeyClient(object):
 
     @pki.handle_exceptions()
     def archive_key(self, client_key_id, data_type, private_data=None,
-                    wrapped_private_data=None,
+                    wrapped_private_data=None, trans_wrapped_session_key=None,
+                    algorithm_oid=None, symkey_params=None,
+                    pki_archive_options=None,
                     key_algorithm=None, key_size=None):
         ''' Archive a secret (symmetric key or passphrase) on the DRM.
 
@@ -505,17 +509,23 @@ class KeyClient(object):
             key_algorithm and key_size are applicable to symmetric keys only.
             If a symmetric key is being archived, these parameters are required.
 
-            wrapped_private_data consists of a PKIArchiveOptions structure, which
-            can be constructed using either generate_archive_options() or
-            generate_pki_archive_options() below.
+            Callers can invoke this method in one of three ways:
+
+            1. Provide the private_data.  This is the secret to be archived.
+               It will be wrapped and sent to the DRM.
+
+            2. Provide the following pieces:
+                - wrapped_private_data - which is the secret wrapped by a session
+                key (168 bit 3DES symmetric key)
+                - trans_wrapped_session_key - the above session key wrapped by the
+                DRM transport certificate public key.
+                - the algorithm_oid string for the symmetric key wrap
+                - the symkey_params for the symmetric key wrap
+
+            3. wrapped_private_data which consists of a PKIArchiveOptions structure,
 
             private_data is the secret that is to be archived.
 
-            Callers must specify EITHER wrapped_private_data OR private_data.
-            If wrapped_private_data is specified, then this data is forwarded to the
-            DRM unchanged.  Otherwise, the private_data is converted to a
-            PKIArchiveOptions structure using the functions below.
-
             The function returns a KeyRequestResponse object containing a KeyRequestInfo
             object with details about the archival request and key archived.
         '''
@@ -527,16 +537,50 @@ class KeyClient(object):
                 raise ValueError(
                         "For symmetric keys, key algorithm and key_size must be specified")
 
-        if wrapped_private_data is None:
-            if private_data is None:
-                raise ValueError("No data provided to be archived")
-            wrapped_private_data = self.generate_archive_options(private_data)
-
-        request = KeyArchivalRequest(client_key_id=client_key_id,
+        if private_data is None:
+            if wrapped_private_data is None:
+                if pki_archive_options is None:
+                    raise ValueError("No data provided to be archived")
+                else:
+                    data = base64.encodestring(pki_archive_options)
+                    request = KeyArchivalRequest(client_key_id=client_key_id,
                                      data_type=data_type,
-                                     wrapped_private_data=wrapped_private_data,
+                                     pki_archive_options=data,
                                      key_algorithm=key_algorithm,
                                      key_size=key_size)
+            if trans_wrapped_session_key is None:
+                raise ValueError("Session Key not provided")
+            else:
+                twsk = base64.encodestring(trans_wrapped_session_key)
+                data = base64.encodestring(wrapped_private_data)
+                request = KeyArchivalRequest(client_key_id=client_key_id,
+                                 data_type=data_type,
+                                 wrapped_private_data=data,
+                                 trans_wrapped_session_key=twsk,
+                                 algorithm_oid=algorithm_oid,
+                                 symkey_params=symkey_params,
+                                 key_algorithm=key_algorithm,
+                                 key_size=key_size)
+        else:
+            session_key = self.crypto.generate_session_key()
+            trans_wrapped_session_key = \
+                self.crypto.asymmetric_wrap(session_key, self.transport_cert)
+            wrapped_private_data = self.crypto.symmetric_wrap(private_data, session_key)
+
+            twsk = base64.encodestring(trans_wrapped_session_key)
+            data = base64.encodestring(wrapped_private_data)
+
+            # TODO - generate_algorithm_oid here
+            # generate symkey_params here
+
+            request = KeyArchivalRequest(client_key_id=client_key_id,
+                            data_type=data_type,
+                            wrapped_private_data=data,
+                            trans_wrapped_session_key=twsk,
+                            algorithm_oid=algorithm_oid,
+                            symkey_params=symkey_params,
+                            key_algorithm=key_algorithm,
+                            key_size=key_size)
         return self.create_request(request)
 
     @pki.handle_exceptions()
@@ -577,9 +621,9 @@ class KeyClient(object):
         if data is None:
             raise ValueError("KeyRecoveryRequest must be specified")
 
-        url = self.keyURL + '/retrieve'
-        keyRequest = json.dumps(data, cls=encoder.CustomTypeEncoder, sort_keys=True)
-        response = self.connection.post(url, keyRequest, self.headers)
+        url = self.key_url + '/retrieve'
+        key_request = json.dumps(data, cls=encoder.CustomTypeEncoder, sort_keys=True)
+        response = self.connection.post(url, key_request, self.headers)
         return KeyData.from_json(response.json())
 
     @pki.handle_exceptions()
diff --git a/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java b/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java
index 3c7628af9141f057332856e3d549c066b8d803e0..03bbfb53a40e3969708d7ee02d479903fb945203 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java
@@ -38,7 +38,17 @@ public class KeyArchivalRequest extends ResourceMessage {
 
     private static final String CLIENT_KEY_ID = "clientKeyID";
     private static final String DATA_TYPE = "dataType";
+
+    // exploded pkiArchiveOptions parameters
     private static final String WRAPPED_PRIVATE_DATA = "wrappedPrivateData";
+    private static final String TRANS_WRAPPED_SESSION_KEY = "transWrappedSessionKey";
+    private static final String ALGORITHM_OID = "algorithmOID";
+    private static final String SYMMETRIC_ALGORITHM_PARAMS = "symmetricAlgorithmParams";
+
+    // parameter for un-exploded pkiArchiveOptions
+    private static final String PKI_ARCHIVE_OPTIONS = "pkiArchiveOptions";
+
+    // parameters for symmetric keys
     private static final String KEY_ALGORITHM = "keyAlgorithm";
     private static final String KEY_SIZE = "keySize";
 
@@ -53,6 +63,8 @@ public class KeyArchivalRequest extends ResourceMessage {
         attributes.put(WRAPPED_PRIVATE_DATA, form.getFirst(WRAPPED_PRIVATE_DATA));
         attributes.put(KEY_ALGORITHM, form.getFirst(KEY_ALGORITHM));
         attributes.put(KEY_SIZE, form.getFirst(KEY_SIZE));
+        attributes.put(PKI_ARCHIVE_OPTIONS, form.getFirst(PKI_ARCHIVE_OPTIONS));
+        attributes.put(TRANS_WRAPPED_SESSION_KEY, form.getFirst(TRANS_WRAPPED_SESSION_KEY));
         setClassName(getClass().getName());
     }
 
@@ -90,7 +102,63 @@ public class KeyArchivalRequest extends ResourceMessage {
     }
 
     /**
-     * @return the wrappedPrivateData
+     * @return the transWrappedSessionKey
+     */
+    public String getTransWrappedSessionKey() {
+        return attributes.get(TRANS_WRAPPED_SESSION_KEY);
+    }
+
+    /**
+     * @param transWrappedSessionKey the trans wrapped session key to set
+     */
+    public void setTransWrappedSessionKey(String twsk) {
+        attributes.put(TRANS_WRAPPED_SESSION_KEY, twsk);
+    }
+
+    /**
+     * @return the PKIArchiveOptions structure
+     */
+    public String getPKIArchiveOptions() {
+        return attributes.get(PKI_ARCHIVE_OPTIONS);
+    }
+
+    /**
+     * @param pkiArchiveIOptions the archive options structure to set
+     */
+    public void setPKIArchiveOptions(String pkiArchiveOptions) {
+        attributes.put(PKI_ARCHIVE_OPTIONS, pkiArchiveOptions);
+    }
+
+    /**
+     * @return the algorithm OID structure
+     */
+    public String getAlgorithmOID() {
+        return attributes.get(ALGORITHM_OID);
+    }
+
+    /**
+     * @param algorithmOID the symmetric algorithm OID to set
+     */
+    public void setAlgorithmOID(String algOID) {
+        attributes.put(ALGORITHM_OID, algOID);
+    }
+
+    /**
+     * @return the algorithm params structure
+     */
+    public String getSymmetricAlgorithmParams() {
+        return attributes.get(SYMMETRIC_ALGORITHM_PARAMS);
+    }
+
+    /**
+     * @param params the algorithm params to set
+     */
+    public void setSymmetricAlgorithmParams(String params) {
+        attributes.put(SYMMETRIC_ALGORITHM_PARAMS, params);
+    }
+
+    /**
+     * @return the pkiArchiveOptions structure
      */
     public String getWrappedPrivateData() {
         return attributes.get(WRAPPED_PRIVATE_DATA);
diff --git a/base/common/src/com/netscape/certsrv/key/KeyClient.java b/base/common/src/com/netscape/certsrv/key/KeyClient.java
index 3c27dbb6cb2276873d43e10f215009d2cad1c99c..0fa1f264e8c3723b5eec02794b5d0b66408a090f 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyClient.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyClient.java
@@ -118,11 +118,28 @@ public class KeyClient extends Client {
         return client.getEntity(response, KeyRequestInfo.class);
     }
 
-    public KeyRequestResponse archiveSecurityData(byte[] encoded, String clientKeyId, String dataType, String algorithm, int strength) {
+    public KeyRequestResponse archiveSecurityData(String clientKeyId, String dataType,
+            String algorithm, int strength, byte[] pkiArchiveOptions) {
         // create archival request
         KeyArchivalRequest data = new KeyArchivalRequest();
-        String req1 = Utils.base64encode(encoded);
-        data.setWrappedPrivateData(req1);
+        data.setPKIArchiveOptions(Utils.base64encode(pkiArchiveOptions));
+        data.setClientKeyId(clientKeyId);
+        data.setDataType(dataType);
+        data.setKeyAlgorithm(algorithm);
+        data.setKeySize(strength);
+
+        return createRequest(data);
+    }
+
+    public KeyRequestResponse archiveSecurityData(String clientKeyId, String dataType,
+            String algorithm, int strength, byte[] wrappedPrivateData,
+            byte[] wrappedSessionKey, String algorithmOID, byte[] algParams) {
+        // create archival request
+        KeyArchivalRequest data = new KeyArchivalRequest();
+        data.setTransWrappedSessionKey(Utils.base64encode(wrappedSessionKey));
+        data.setWrappedPrivateData(Utils.base64encode(wrappedPrivateData));
+        data.setAlgorithmOID(algorithmOID);
+        data.setSymmetricAlgorithmParams(Utils.base64encode(algParams));
         data.setClientKeyId(clientKeyId);
         data.setDataType(dataType);
         data.setKeyAlgorithm(algorithm);
diff --git a/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java b/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
index a2767ee53f3e28f14731474859e9dea9c91c8fa4..69a39d7e23232a1a0cc6e2fe98305d452234bb8c 100644
--- a/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
+++ b/base/common/src/com/netscape/certsrv/profile/IEnrollProfile.java
@@ -155,6 +155,26 @@ public interface IEnrollProfile extends IProfile {
     public static final String REQUEST_TRANSPORT_CERT = "req_transport_cert";
 
     /**
+     * Session wrapped security data passed in to the DRM archival service
+     */
+    public static final String REQUEST_SECURITY_DATA = "req_security_data";
+
+    /**
+     * Transport Key wrapped session key passed into DRM archival service.
+     */
+    public static final String REQUEST_SESSION_KEY = "req_session_key";
+
+    /**
+     * Symmetric Key algorithm OID passed into DRM archival service
+     */
+    public static final String REQUEST_ALGORITHM_OID = "req_algorithm_oid";
+
+    /**
+     * Symmetric key algorithm params passed into DRM archival service
+     */
+    public static final String REQUEST_ALGORITHM_PARAMS = "req_algorithm_params";
+
+    /**
      * Set Default X509CertInfo in the request.
      *
      * @param request profile-based certificate request.
diff --git a/base/kra/functional/drmtest.py b/base/kra/functional/drmtest.py
index dd7abbf53e37e720c002d991cd097cc02e5b8ebf..08baf5011e25f36bf8ed9b898e7140b34bbc4ae4 100644
--- a/base/kra/functional/drmtest.py
+++ b/base/kra/functional/drmtest.py
@@ -106,7 +106,7 @@ def main():
     # Test 4: generate symkey -- same as barbican_encode()
     print "Now generating symkey on KRA"
     #client_key_id = "Vek #1" + time.strftime('%X %x %Z')
-    client_key_id = "veka6"
+    client_key_id = "veka9"
     algorithm = "AES"
     key_size = 128
     usages = [key.SymKeyGenerationRequest.DECRYPT_USAGE, key.SymKeyGenerationRequest.ENCRYPT_USAGE]
@@ -132,6 +132,7 @@ def main():
     # Test 6: Barbican_decode() - Retrieve while providing trans_wrapped_session_key
     session_key = crypto.generate_session_key()
     wrapped_session_key = crypto.asymmetric_wrap(session_key, keyclient.transport_cert)
+    print "My key id is " + str(key_id)
     key_data, _unwrapped_key = keyclient.retrieve_key(key_id, trans_wrapped_session_key=wrapped_session_key)
     print_key_data(key_data)
     unwrapped_key = crypto.symmetric_unwrap(base64.decodestring(key_data.wrappedPrivateData),
@@ -211,5 +212,17 @@ def main():
     response = keyclient.generate_symmetric_key(client_key_id)
     print_key_request(response.requestInfo)
 
+    # Test 19: Try to archive key
+    print "try to archive key"
+    print "key to archive: " + key1
+    client_key_id = "Vek #4" + time.strftime('%X %x %Z')
+
+    # this test is not quite working yet
+    #response = keyclient.archive_key(client_key_id, keyclient.SYMMETRIC_KEY_TYPE,
+    #                                private_data=base64.decodestring(key1),
+    #                                key_algorithm=keyclient.AES_ALGORITHM,
+    #                                key_size=128)
+    #print_key_request(response.requestInfo)
+
 if __name__ == "__main__":
     main()
diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
index 5681c1114261f399f48d9f711dc43258e5e6df10..899c78a66db17ea11ae80565335879c0676c9725 100644
--- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
+++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
@@ -263,8 +263,8 @@ public class DRMTest {
             byte[] encoded = CryptoUtil.createPKIArchiveOptions(manager, token, transportCert, vek, null,
                     KeyGenAlgorithm.DES3, ivps);
 
-            KeyRequestResponse info = keyClient.archiveSecurityData(encoded, clientKeyId,
-                    KeyRequestResource.SYMMETRIC_KEY_TYPE, KeyRequestResource.DES3_ALGORITHM, 0);
+            KeyRequestResponse info = keyClient.archiveSecurityData(clientKeyId, KeyRequestResource.SYMMETRIC_KEY_TYPE,
+                    KeyRequestResource.DES3_ALGORITHM, 0, encoded);
             log("Archival Results:");
             printRequestInfo(info.getRequestInfo());
             keyId = info.getRequestInfo().getKeyId();
@@ -375,8 +375,8 @@ public class DRMTest {
         try {
             byte[] encoded = CryptoUtil.createPKIArchiveOptions(manager, token, transportCert, null, passphrase,
                     KeyGenAlgorithm.DES3, ivps);
-            requestResponse = keyClient.archiveSecurityData(encoded, clientKeyId,
-                    KeyRequestResource.PASS_PHRASE_TYPE, null, 0);
+            requestResponse = keyClient.archiveSecurityData(clientKeyId, KeyRequestResource.PASS_PHRASE_TYPE,
+                    null, 0, encoded);
             log("Archival Results:");
             printRequestInfo(requestResponse.getRequestInfo());
             keyId = requestResponse.getRequestInfo().getKeyId();
@@ -661,8 +661,8 @@ public class DRMTest {
             byte[] encoded = CryptoUtil.createPKIArchiveOptions(manager, token, transportCert, vek, null,
                     KeyGenAlgorithm.DES3, ivps);
 
-            KeyRequestResponse response = keyClient.archiveSecurityData(encoded, clientKeyId,
-                    KeyRequestResource.SYMMETRIC_KEY_TYPE, KeyRequestResource.AES_ALGORITHM, 128);
+            KeyRequestResponse response = keyClient.archiveSecurityData(clientKeyId, KeyRequestResource.SYMMETRIC_KEY_TYPE,
+                    KeyRequestResource.AES_ALGORITHM, 128, encoded);
             log("Archival Results:");
             printRequestInfo(response.getRequestInfo());
             keyId = response.getRequestInfo().getKeyId();
diff --git a/base/kra/src/com/netscape/kra/SecurityDataService.java b/base/kra/src/com/netscape/kra/SecurityDataService.java
index 388079f32fd56d3adbc0c7452f1b7a59693a16b1..37229f09af4a4a6132521cfcb4730035820fec65 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataService.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataService.java
@@ -84,7 +84,17 @@ public class SecurityDataService implements IService {
             throws EBaseException {
         String id = request.getRequestId().toString();
         String clientKeyId = request.getExtDataInString(IRequest.SECURITY_DATA_CLIENT_KEY_ID);
-        String wrappedSecurityData = request.getExtDataInString(IEnrollProfile.REQUEST_ARCHIVE_OPTIONS);
+
+        // one way to get data - unexploded pkiArchiveOptions
+        String pkiArchiveOptions = request.getExtDataInString(IEnrollProfile.REQUEST_ARCHIVE_OPTIONS);
+
+        // another way - exploded pkiArchiveOptions
+        String transWrappedSessionKey = request.getExtDataInString(IEnrollProfile.REQUEST_SESSION_KEY);
+        String wrappedSecurityData = request.getExtDataInString(IEnrollProfile.REQUEST_SECURITY_DATA);
+        String algParams = request.getExtDataInString(IEnrollProfile.REQUEST_ALGORITHM_PARAMS);
+        String algStr = request.getExtDataInString(IEnrollProfile.REQUEST_ALGORITHM_OID);
+
+        // prameters if the secret is a symkey
         String dataType = request.getExtDataInString(IRequest.SECURITY_DATA_TYPE);
         String algorithm = request.getExtDataInString(IRequest.SECURITY_DATA_ALGORITHM);
         int strength = request.getExtDataInInteger(IRequest.SECURITY_DATA_STRENGTH);
@@ -96,25 +106,50 @@ public class SecurityDataService implements IService {
         String subjectID = auditSubjectID();
 
         //Check here even though restful layer checks for this.
-        if(wrappedSecurityData == null || clientKeyId == null || dataType == null) {
+        if (clientKeyId == null || dataType == null) {
             auditArchivalRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(),
                     clientKeyId, null, "Bad data in request");
             throw new EBaseException("Bad data in SecurityDataService.serviceRequest");
         }
-        //We need some info from the PKIArchiveOptions wrapped security data
 
-        byte[] encoded = Utils.base64decode(wrappedSecurityData);
+        if (wrappedSecurityData != null) {
+            if (transWrappedSessionKey == null || algStr == null || algParams == null) {
+                throw new EBaseException(
+                        "Bad data in SecurityDataService.serviceRequest, no session key");
 
-        ArchiveOptions options = ArchiveOptions.toArchiveOptions(encoded);
-
-        //Check here just in case a null ArchiveOptions makes it this far
-        if(options == null) {
-            auditArchivalRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(),
-                    clientKeyId, null, "Problem decoding PKIArchiveOptions");
-            throw new EBaseException("Problem decoding PKIArchiveOptions.");
+            }
+        } else if (pkiArchiveOptions == null) {
+            throw new EBaseException("No data to archive in SecurityDataService.serviceRequest");
         }
 
-        String algStr = options.getSymmAlgOID();
+        byte[] wrappedSessionKey = null;
+        byte[] secdata = null;
+        byte[] sparams = null;
+
+        if (wrappedSecurityData == null) {
+            // We have PKIArchiveOptions data
+
+            //We need some info from the PKIArchiveOptions wrapped security data
+            byte[] encoded = Utils.base64decode(pkiArchiveOptions);
+
+            ArchiveOptions options = ArchiveOptions.toArchiveOptions(encoded);
+
+            //Check here just in case a null ArchiveOptions makes it this far
+            if (options == null) {
+                auditArchivalRequestProcessed(subjectID, ILogger.FAILURE, request.getRequestId(),
+                        clientKeyId, null, "Problem decoding PKIArchiveOptions");
+                throw new EBaseException("Problem decoding PKIArchiveOptions.");
+            }
+            algStr = options.getSymmAlgOID();
+            wrappedSessionKey = options.getEncSymmKey();
+            secdata = options.getEncValue();
+            sparams = options.getSymmAlgParams();
+
+        } else {
+            wrappedSessionKey = Utils.base64decode(transWrappedSessionKey);
+            secdata = Utils.base64decode(wrappedSecurityData);
+            sparams = Utils.base64decode(algParams);
+        }
 
         SymmetricKey securitySymKey = null;
         byte[] securityData = null;
@@ -123,19 +158,21 @@ public class SecurityDataService implements IService {
         if (dataType.equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
             // Symmetric Key
             keyType = KeyRequestResource.SYMMETRIC_KEY_TYPE;
-            securitySymKey = mTransportUnit.unwrap_symmetric(options.getEncSymmKey(),
-                      options.getSymmAlgOID(),
-                      options.getSymmAlgParams(),
-                      options.getEncValue(),
-                      KeyRequestService.SYMKEY_TYPES.get(algorithm),
-                      strength);
+            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;
-            securityData = mTransportUnit.decryptExternalPrivate(options.getEncSymmKey(),
-                      options.getSymmAlgOID(),
-                      options.getSymmAlgParams(),
-                      options.getEncValue());
+            securityData = mTransportUnit.decryptExternalPrivate(
+                    wrappedSessionKey,
+                    algStr,
+                    sparams,
+                    secdata);
 
         }
 
diff --git a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java
index d84bbd013d16fbea214ce28a9046eae2d8737410..93e0018c429035662145c741a10081645808f9cd 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/key/KeyRequestDAO.java
@@ -63,6 +63,11 @@ import com.netscape.cms.servlet.request.KeyRequestService;
 public class KeyRequestDAO extends CMSRequestDAO {
 
     private static String REQUEST_ARCHIVE_OPTIONS = IEnrollProfile.REQUEST_ARCHIVE_OPTIONS;
+    private static String REQUEST_SECURITY_DATA = IEnrollProfile.REQUEST_SECURITY_DATA;
+    private static String REQUEST_SESSION_KEY = IEnrollProfile.REQUEST_SESSION_KEY;
+    private static String REQUEST_ALGORITHM_OID = IEnrollProfile.REQUEST_ALGORITHM_OID;
+    private static String REQUEST_ALGORITHM_PARAMS = IEnrollProfile.REQUEST_ALGORITHM_PARAMS;
+
     public static final String ATTR_SERIALNO = "serialNumber";
 
     private IKeyRepository repo;
@@ -140,6 +145,10 @@ public class KeyRequestDAO extends CMSRequestDAO {
     public KeyRequestResponse submitRequest(KeyArchivalRequest data, UriInfo uriInfo) throws EBaseException {
         String clientKeyId = data.getClientKeyId();
         String wrappedSecurityData = data.getWrappedPrivateData();
+        String transWrappedSessionKey = data.getTransWrappedSessionKey();
+        String algorithmOID = data.getAlgorithmOID();
+        String symkeyParams = data.getSymmetricAlgorithmParams();
+        String pkiArchiveOptions = data.getPKIArchiveOptions();
         String dataType = data.getDataType();
         String keyAlgorithm = data.getKeyAlgorithm();
         int keyStrength = data.getKeySize();
@@ -152,7 +161,14 @@ public class KeyRequestDAO extends CMSRequestDAO {
 
         IRequest request = queue.newRequest(IRequest.SECURITY_DATA_ENROLLMENT_REQUEST);
 
-        request.setExtData(REQUEST_ARCHIVE_OPTIONS, wrappedSecurityData);
+        if (pkiArchiveOptions != null) {
+            request.setExtData(REQUEST_ARCHIVE_OPTIONS, pkiArchiveOptions);
+        } else {
+            request.setExtData(REQUEST_SECURITY_DATA, wrappedSecurityData);
+            request.setExtData(REQUEST_SESSION_KEY, transWrappedSessionKey);
+            request.setExtData(REQUEST_ALGORITHM_PARAMS, symkeyParams);
+            request.setExtData(REQUEST_ALGORITHM_OID, algorithmOID);
+        }
         request.setExtData(IRequest.SECURITY_DATA_CLIENT_KEY_ID, clientKeyId);
         request.setExtData(IRequest.SECURITY_DATA_TYPE, dataType);
         request.setExtData(IRequest.SECURITY_DATA_STRENGTH,
diff --git a/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java b/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java
index dd27c2ac77bf34aa728c1f66e3d0629974c36b79..e2253b6e8bf8a9273e0cd106e06612bb8f333620 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/request/KeyRequestService.java
@@ -162,12 +162,23 @@ public class KeyRequestService extends PKIService implements KeyRequestResource
         // auth and authz
         // Catch this before internal server processing has to deal with it
 
-        if (data == null || data.getClientKeyId() == null
-                || data.getWrappedPrivateData() == null
-                || data.getDataType() == null) {
+        if (data == null || data.getClientKeyId() == null || data.getDataType() == null) {
             throw new BadRequestException("Invalid key archival request.");
         }
 
+        if (data.getWrappedPrivateData() != null) {
+            if (data.getTransWrappedSessionKey() == null ||
+                data.getAlgorithmOID() == null ||
+                data.getSymmetricAlgorithmParams() == null) {
+                throw new BadRequestException(
+                        "Invalid key archival request.  " +
+                        "Missing wrapped session key, algoriithmOIS or symmetric key parameters");
+            }
+        } else if (data.getPKIArchiveOptions() == null) {
+            throw new BadRequestException(
+                    "Invalid key archival request.  No data to archive");
+        }
+
         if (data.getDataType().equals(KeyRequestResource.SYMMETRIC_KEY_TYPE)) {
             if ((data.getKeyAlgorithm() == null) ||
                 (! SYMKEY_TYPES.containsKey(data.getKeyAlgorithm()))) {
-- 
1.8.4.2

