From a20052eceb1ef70d8043c1e8417092d82e23f789 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 2 Nov 2016 11:20:34 -0400
Subject: [PATCH 332/333] Add method to obtain synchronous request ids

This will help to distinguish local synchronous requests.
These requests will not be stored in LDAP, but otherwise will
be treated the same as normal requests.

Add express parameter to archival and recovery requests

Use that parameter to get a synchronous request,
and then execute the archival/ recovery code synchronously.

Add code to run synchronous archival and retrieval requests
Added java client pieces

Also fixed up the key retrieval mechanism.
The recover() method is to create a recovery request, not to
synchronously create a key.
---
 .../netscape/certsrv/key/KeyArchivalRequest.java   |  23 ++
 .../src/com/netscape/certsrv/key/KeyClient.java    | 105 +++++--
 .../netscape/certsrv/key/KeyRecoveryRequest.java   |  19 ++
 .../certsrv/kra/IKeyRecoveryAuthority.java         |  10 +-
 .../netscape/certsrv/request/IRequestQueue.java    |  12 +
 .../com/netscape/certsrv/request/RequestId.java    |   4 +
 .../com/netscape/cmstools/key/KeyArchiveCLI.java   |  13 +-
 .../com/netscape/cmstools/key/KeyRetrieveCLI.java  |  22 +-
 .../src/com/netscape/kra/KeyRecoveryAuthority.java |  19 ++
 .../com/netscape/kra/SecurityDataProcessor.java    |   6 +-
 .../netscape/kra/SecurityDataRecoveryService.java  |   1 -
 .../server/kra/rest/KeyRequestService.java         |  11 -
 .../org/dogtagpki/server/kra/rest/KeyService.java  | 331 +++++++++------------
 .../netscape/cms/servlet/key/KeyRequestDAO.java    |  69 +++--
 .../netscape/cmscore/request/ARequestQueue.java    |  20 +-
 .../com/netscape/cmscore/request/RequestQueue.java |   8 +
 16 files changed, 408 insertions(+), 265 deletions(-)

diff --git a/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java b/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java
index 67810a02f0847942fee7466553363ae9e75c8a13..a15a2fb1b89aa252c4596934178ac4d0209fbe63 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyArchivalRequest.java
@@ -55,6 +55,9 @@ public class KeyArchivalRequest extends ResourceMessage {
     // parameters to set realm
     private static final String REALM = "realm";
 
+    // parameter to specify an express request
+    private static final String EXPRESS = "express";
+
     public KeyArchivalRequest() {
         // required for JAXB (defaults)
         setClassName(getClass().getName());
@@ -69,6 +72,7 @@ public class KeyArchivalRequest extends ResourceMessage {
         attributes.put(PKI_ARCHIVE_OPTIONS, form.getFirst(PKI_ARCHIVE_OPTIONS));
         attributes.put(TRANS_WRAPPED_SESSION_KEY, form.getFirst(TRANS_WRAPPED_SESSION_KEY));
         attributes.put(REALM, form.getFirst(REALM));
+        attributes.put(EXPRESS, form.getFirst(EXPRESS));
         setClassName(getClass().getName());
     }
 
@@ -221,6 +225,25 @@ public class KeyArchivalRequest extends ResourceMessage {
         }
     }
 
+    /**
+     * @return if the request is an express request
+     */
+    public boolean isExpress() {
+        if (attributes.get(EXPRESS) != null) {
+            return attributes.get(EXPRESS).equalsIgnoreCase("true");
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @param express - is this an express request?
+     */
+    public void setExpress(boolean express) {
+        attributes.put(EXPRESS, Boolean.toString(express));
+    }
+
+
     public String toString() {
         try {
             return ResourceMessage.marshal(this, KeyArchivalRequest.class);
diff --git a/base/common/src/com/netscape/certsrv/key/KeyClient.java b/base/common/src/com/netscape/certsrv/key/KeyClient.java
index cb35922f6978b87823d3033a4b807f945842712a..c9abea5acc7f8f253aff640072abdf805b97c5b9 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyClient.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyClient.java
@@ -354,19 +354,20 @@ public class KeyClient extends Client {
      * on the client side i.e. the secret is not transmitted as it is by the server.)
      *
      * @param keyId -- key id for secret
+     * @param express -- express request
      * @return A Key object containing the unwrapped secret (set to the attribute data).
      * @throws Exception - Exceptions of type NoSuchAlgorithmException, IllegalStateException, TokenException,
      *             CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
-    public Key retrieveKey(KeyId keyId) throws Exception {
+    public Key retrieveKey(KeyId keyId, boolean express) throws Exception {
         if (keyId == null) {
             throw new IllegalArgumentException("KeyId must be specified.");
         }
         SymmetricKey sessionKey = crypto.generateSessionKey();
         byte[] transWrappedSessionKey = crypto.wrapSessionKeyWithTransportCert(sessionKey, transportCert);
 
-        Key data = retrieveKey(keyId, transWrappedSessionKey);
+        Key data = retrieveKey(keyId, transWrappedSessionKey, express);
 
         data.setData(crypto.unwrapWithSessionKey(data.getEncryptedData(), sessionKey,
                 KeyRequestResource.DES3_ALGORITHM, data.getNonceData()));
@@ -374,6 +375,10 @@ public class KeyClient extends Client {
         return data;
     }
 
+    public Key retrieveKey(KeyId keyId) throws Exception {
+        return retrieveKey(keyId, false);
+    }
+
     /**
      * Retrieve a secret (passphrase or symmetric key) from the DRM.
      *
@@ -388,12 +393,13 @@ public class KeyClient extends Client {
      *
      * @param keyId -- key id for secret
      * @param transWrappedSessionKey -- session key wrapped by the transport cert.
+     * @param express -- express request
      * @return A Key object containing the wrapped secret.
      * @throws Exception - Exceptions of type NoSuchAlgorithmException, IllegalStateException, TokenException,
      *             CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
-    public Key retrieveKey(KeyId keyId, byte[] transWrappedSessionKey) throws Exception {
+    public Key retrieveKey(KeyId keyId, byte[] transWrappedSessionKey, boolean express) throws Exception {
 
         if (keyId == null) {
             throw new IllegalArgumentException("KeyId must be specified.");
@@ -402,17 +408,27 @@ public class KeyClient extends Client {
             throw new IllegalArgumentException("A transport cert wrapped session key cannot be null.");
         }
 
-        KeyRequestResponse keyData = recoverKey(keyId, null, null, null, null);
-        approveRequest(keyData.getRequestId());
-
         KeyRecoveryRequest recoveryRequest = new KeyRecoveryRequest();
+
+        if (!express) {
+            // create and approve a recovery request
+            KeyRequestResponse keyData = recoverKey(keyId, null, null, null, null);
+            approveRequest(keyData.getRequestId());
+            recoveryRequest.setRequestId(keyData.getRequestId());
+
+        }
+
         recoveryRequest.setKeyId(keyId);
-        recoveryRequest.setRequestId(keyData.getRequestId());
         recoveryRequest.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey));
+        recoveryRequest.setExpress(express);
 
         return retrieveKeyData(recoveryRequest);
     }
 
+    public Key retrieveKey(KeyId keyId, byte[] transWrappedSessionKey) throws Exception {
+        return retrieveKey(keyId, transWrappedSessionKey, false);
+    }
+
     /**
      *
      * The secret is secured in transit by wrapping the secret with the passphrase using
@@ -427,12 +443,13 @@ public class KeyClient extends Client {
      *
      * @param keyId -- key id of secret.
      * @param passphrase -- passphrase used to wrap the secret in the response.
+     * @param express -- express request
      * @return A Key object with the secret wrapped with the passphrase.
      * @throws Exception - Exceptions of type NoSuchAlgorithmException, IllegalStateException, TokenException,
      *             CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
-    public Key retrieveKeyByPassphrase(KeyId keyId, String passphrase) throws Exception {
+    public Key retrieveKeyByPassphrase(KeyId keyId, String passphrase, boolean express) throws Exception {
         if (keyId == null) {
             throw new IllegalArgumentException("KeyId must be specified.");
         }
@@ -445,7 +462,11 @@ public class KeyClient extends Client {
         byte[] sessionWrappedPassphrase = crypto.wrapWithSessionKey(passphrase, nonceData, sessionKey,
                 KeyRequestResource.DES3_ALGORITHM);
 
-        return retrieveKeyUsingWrappedPassphrase(keyId, transWrappedSessionKey, sessionWrappedPassphrase, nonceData);
+        return retrieveKeyUsingWrappedPassphrase(keyId, transWrappedSessionKey, sessionWrappedPassphrase, nonceData, express);
+    }
+
+    public Key retrieveKeyByPassphrase(KeyId keyId, String passphrase) throws Exception {
+        return retrieveKeyByPassphrase(keyId, passphrase, false);
     }
 
     /**
@@ -461,13 +482,14 @@ public class KeyClient extends Client {
      * @param transWrappedSessionKey -- Session key wrapped with the transport cert
      * @param sessionWrappedPassphrase -- Passphrase wrapped with the session key
      * @param nonceData -- nonce data used for encryption.
+     * @param express -- express request
      * @return A Key object with the secret wrapped by the passphrase provided.
      * @throws Exception - Exceptions of type NoSuchAlgorithmException, IllegalStateException, TokenException,
      *             CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
     public Key retrieveKeyUsingWrappedPassphrase(KeyId keyId, byte[] transWrappedSessionKey,
-            byte[] sessionWrappedPassphrase, byte[] nonceData) throws Exception {
+            byte[] sessionWrappedPassphrase, byte[] nonceData, boolean express) throws Exception {
 
         if (keyId == null) {
             throw new IllegalArgumentException("KeyId has to be specified.");
@@ -491,6 +513,7 @@ public class KeyClient extends Client {
         KeyRecoveryRequest data = new KeyRecoveryRequest();
         data.setKeyId(keyId);
         data.setRequestId(requestId);
+        data.setExpress(express);
         if (transWrappedSessionKey != null) {
             data.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey));
         }
@@ -506,6 +529,12 @@ public class KeyClient extends Client {
         return retrieveKeyData(data);
     }
 
+    public Key retrieveKeyUsingWrappedPassphrase(KeyId keyId, byte[] transWrappedSessionKey,
+            byte[] sessionWrappedPassphrase, byte[] nonceData) throws Exception {
+        return retrieveKeyUsingWrappedPassphrase(keyId, transWrappedSessionKey,
+                sessionWrappedPassphrase, nonceData, false);
+    }
+
     /**
      * Retrieve an asymmetric private key and return it as PKCS12 data.
      *
@@ -516,9 +545,10 @@ public class KeyClient extends Client {
      * @param keyId -- key id for secret
      * @param certificate -- the certificate associated with the private key
      * @param passphrase -- A passphrase for the pkcs12 file.
+     * @param express -- express
      * @return A Key object with the wrapped secret
      */
-    public Key retrieveKeyByPKCS12(KeyId keyId, String certificate, String passphrase) {
+    public Key retrieveKeyByPKCS12(KeyId keyId, String certificate, String passphrase, boolean express) {
         if (keyId == null || certificate == null || passphrase == null) {
             throw new IllegalArgumentException("KeyId, certificate and passphrase must be specified.");
         }
@@ -529,10 +559,15 @@ public class KeyClient extends Client {
         recoveryRequest.setKeyId(keyId);
         recoveryRequest.setRequestId(keyData.getRequestId());
         recoveryRequest.setPassphrase(passphrase);
+        recoveryRequest.setExpress(express);
 
         return retrieveKeyData(recoveryRequest);
     }
 
+    public Key retrieveKeyByPKCS12(KeyId keyId, String certificate, String passphrase) {
+        return retrieveKeyByPKCS12(keyId, certificate, passphrase, false);
+    }
+
     /**
      * Archive a passphrase on the DRM.
      *
@@ -544,13 +579,14 @@ public class KeyClient extends Client {
      * @param clientKeyId -- Client Key Identfier
      * @param passphrase -- Secret passphrase to be archived
      * @param realm -- authorization realm
+     * @param express -- express request
      * @return A KeyRequestResponse object with information about the request.
      * @throws Exception - Exceptions of type NoSuchAlgorithmException, IllegalStateException, TokenException,
      *             IOException, CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
-    public KeyRequestResponse archivePassphrase(String clientKeyId, String passphrase, String realm) throws Exception {
-
+    public KeyRequestResponse archivePassphrase(String clientKeyId, String passphrase, String realm, boolean express)
+            throws Exception {
         // Default algorithm OID for DES_EDE3_CBC
         String algorithmOID = EncryptionAlgorithm.DES3_CBC.toOID().toString();
         byte[] nonceData = CryptoUtil.getNonceData(8);
@@ -560,13 +596,17 @@ public class KeyClient extends Client {
                 sessionKey, KeyRequestResource.DES3_ALGORITHM);
 
         return archiveEncryptedData(clientKeyId, KeyRequestResource.PASS_PHRASE_TYPE, null, 0, algorithmOID,
-                nonceData, encryptedData, transWrappedSessionKey, realm);
+                nonceData, encryptedData, transWrappedSessionKey, realm, express);
+    }
+
+    public KeyRequestResponse archivePassphrase(String clientKeyId, String passphrase, String realm) throws Exception {
+        return archivePassphrase(clientKeyId, passphrase, null, false);
     }
 
     /* Old signature for backwards compatibility */
     @Deprecated
     public KeyRequestResponse archivePassphrase(String clientKeyId, String passphrase) throws Exception {
-        return archivePassphrase(clientKeyId, passphrase, null);
+        return archivePassphrase(clientKeyId, passphrase, null, false);
     }
 
     /**
@@ -580,13 +620,14 @@ public class KeyClient extends Client {
      * @param keyAlgorithm -- Algorithm used by the symmetric key
      * @param keySize -- Strength of the symmetric key (secret)
      * @param realm -- authorization realm
+     * @param express -- express request
      * @return A KeyRequestResponse object with information about the request.
      * @throws Exception - Exceptions of type NoSuchAlgorithmException, IllegalStateException, TokenException,
      *             IOException, CertificateEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
      *             BadPaddingException, IllegalBlockSizeException
      */
     public KeyRequestResponse archiveSymmetricKey(String clientKeyId, SymmetricKey secret, String keyAlgorithm,
-            int keySize, String realm) throws Exception {
+            int keySize, String realm, boolean express) throws Exception {
 
         // Default algorithm OID for DES_EDE3_CBC
         String algorithmOID = EncryptionAlgorithm.DES3_CBC.toOID().toString();
@@ -596,14 +637,19 @@ public class KeyClient extends Client {
         byte[] transWrappedSessionKey = crypto.wrapSessionKeyWithTransportCert(sessionKey, this.transportCert);
 
         return archiveEncryptedData(clientKeyId, KeyRequestResource.SYMMETRIC_KEY_TYPE, keyAlgorithm, keySize,
-                algorithmOID, nonceData, encryptedData, transWrappedSessionKey, realm);
+                algorithmOID, nonceData, encryptedData, transWrappedSessionKey, realm, express);
+    }
+
+    public KeyRequestResponse archiveSymmetricKey(String clientKeyId, SymmetricKey secret, String keyAlgorithm,
+            int keySize, String realm) throws Exception {
+        return archiveSymmetricKey(clientKeyId, secret, keyAlgorithm,keySize, null, false);
     }
 
     /* old method signature for backwards compatibility */
     @Deprecated
     public KeyRequestResponse archiveSymmetricKey(String clientKeyId, SymmetricKey secret, String keyAlgorithm,
             int keySize) throws Exception {
-        return archiveSymmetricKey(clientKeyId, secret, keyAlgorithm,keySize, null);
+        return archiveSymmetricKey(clientKeyId, secret, keyAlgorithm,keySize, null, false);
     }
 
     /**
@@ -627,7 +673,7 @@ public class KeyClient extends Client {
      */
     public KeyRequestResponse archiveEncryptedData(String clientKeyId, String dataType, String keyAlgorithm,
             int keySize, String algorithmOID, byte[] nonceData, byte[] encryptedData,
-            byte[] transWrappedSessionKey, String realm) {
+            byte[] transWrappedSessionKey, String realm, boolean express) {
 
         if (clientKeyId == null || dataType == null) {
             throw new IllegalArgumentException("Client key id and data type must be specified.");
@@ -654,17 +700,25 @@ public class KeyClient extends Client {
         data.setWrappedPrivateData(req1);
         data.setTransWrappedSessionKey(Utils.base64encode(transWrappedSessionKey));
         data.setRealm(realm);
+        data.setExpress(express);
 
         return submitRequest(data);
     }
 
+    public KeyRequestResponse archiveEncryptedData(String clientKeyId, String dataType, String keyAlgorithm,
+            int keySize, String algorithmOID, byte[] nonceData, byte[] encryptedData,
+            byte[] transWrappedSessionKey, String realm) {
+        return archiveEncryptedData(clientKeyId, dataType, keyAlgorithm, keySize, algorithmOID, nonceData,
+                encryptedData, transWrappedSessionKey, realm, false);
+    }
+
     /* old signature for backwards compatibility */
     @Deprecated
     public KeyRequestResponse archiveEncryptedData(String clientKeyId, String dataType, String keyAlgorithm,
             int keySize, String algorithmOID, byte[] nonceData, byte[] encryptedData,
             byte[] transWrappedSessionKey) {
         return archiveEncryptedData(clientKeyId, dataType, keyAlgorithm, keySize, algorithmOID, nonceData,
-                encryptedData, transWrappedSessionKey, null);
+                encryptedData, transWrappedSessionKey, null, false);
     }
 
     /**
@@ -677,11 +731,12 @@ public class KeyClient extends Client {
      * @param pkiArchiveOptions -- is the data to be archived wrapped in a
      *            PKIArchiveOptions structure
      * @param realm -- authorization realm
+     * @param express -- express request
      * @return A KeyRequestResponse object with information about the request.
      * @throws Exception
      */
     public KeyRequestResponse archivePKIOptions(String clientKeyId, String dataType, String keyAlgorithm, int keySize,
-            byte[] pkiArchiveOptions, String realm) {
+            byte[] pkiArchiveOptions, String realm, boolean express) {
 
         if (clientKeyId == null || dataType == null) {
             throw new IllegalArgumentException("Client key id and data type must be specified.");
@@ -706,15 +761,21 @@ public class KeyClient extends Client {
         String options = Utils.base64encode(pkiArchiveOptions);
         data.setPKIArchiveOptions(options);
         data.setRealm(realm);
+        data.setExpress(express);
 
         return submitRequest(data);
     }
 
+    public KeyRequestResponse archivePKIOptions(String clientKeyId, String dataType, String keyAlgorithm, int keySize,
+            byte[] pkiArchiveOptions, String realm) {
+        return archivePKIOptions(clientKeyId, dataType, keyAlgorithm, keySize, pkiArchiveOptions, null, false);
+    }
+
     /* old method signature for backwards compatibility */
     @Deprecated
     public KeyRequestResponse archivePKIOptions(String clientKeyId, String dataType, String keyAlgorithm, int keySize,
             byte[] pkiArchiveOptions) {
-        return archivePKIOptions(clientKeyId, dataType, keyAlgorithm, keySize, pkiArchiveOptions, null);
+        return archivePKIOptions(clientKeyId, dataType, keyAlgorithm, keySize, pkiArchiveOptions, null, false);
     }
 
     /**
diff --git a/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java b/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java
index e12b414b139f61d721286a359587ad240ed82bf5..45bb52501ddb7607724ac59154dd9a9287aed11a 100644
--- a/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java
+++ b/base/common/src/com/netscape/certsrv/key/KeyRecoveryRequest.java
@@ -45,6 +45,7 @@ public class KeyRecoveryRequest extends ResourceMessage {
     private static final String NONCE_DATA = "nonceData";
     private static final String CERTIFICATE = "certificate";
     private static final String PASSPHRASE = "passphrase";
+    private static final String EXPRESS = "express";
 
     public KeyRecoveryRequest() {
         // required for JAXB (defaults)
@@ -63,6 +64,7 @@ public class KeyRecoveryRequest extends ResourceMessage {
         attributes.put(NONCE_DATA, form.getFirst(NONCE_DATA));
         attributes.put(CERTIFICATE, form.getFirst(CERTIFICATE));
         attributes.put(PASSPHRASE, form.getFirst(PASSPHRASE));
+        attributes.put(EXPRESS, form.getFirst(EXPRESS));
         setClassName(getClass().getName());
     }
 
@@ -171,6 +173,23 @@ public class KeyRecoveryRequest extends ResourceMessage {
         attributes.put(PASSPHRASE, passphrase);
     }
 
+    /**
+     * @return if the request is an express request
+     */
+    public boolean isExpress() {
+        if (attributes.get(EXPRESS) != null) {
+            return attributes.get(EXPRESS).equalsIgnoreCase("true");
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @param express - is this an express request?
+     */
+    public void setExpress(boolean express) {
+        attributes.put(EXPRESS, Boolean.toString(express));
+    }
 
     public static KeyRecoveryRequest valueOf(String string) throws Exception {
         try {
diff --git a/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java b/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java
index acc3214bbec24baa777426bec337fed0943c9c8b..40a084ce8ab225482e528084e3f3212c080b31ee 100644
--- a/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java
+++ b/base/common/src/com/netscape/certsrv/kra/IKeyRecoveryAuthority.java
@@ -21,8 +21,6 @@ import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Vector;
 
-import netscape.security.x509.X500Name;
-
 import org.mozilla.jss.crypto.CryptoToken;
 
 import com.netscape.certsrv.base.EBaseException;
@@ -30,6 +28,7 @@ import com.netscape.certsrv.base.ISubsystem;
 import com.netscape.certsrv.dbs.keydb.IKeyRepository;
 import com.netscape.certsrv.dbs.replicadb.IReplicaIDRepository;
 import com.netscape.certsrv.policy.IPolicyProcessor;
+import com.netscape.certsrv.request.IRequest;
 import com.netscape.certsrv.request.IRequestListener;
 import com.netscape.certsrv.request.IRequestQueue;
 import com.netscape.certsrv.request.RequestId;
@@ -37,6 +36,8 @@ import com.netscape.certsrv.security.Credential;
 import com.netscape.certsrv.security.IStorageKeyUnit;
 import com.netscape.certsrv.security.ITransportKeyUnit;
 
+import netscape.security.x509.X500Name;
+
 /**
  * An interface represents key recovery authority. The
  * key recovery authority is responsibile for archiving
@@ -318,4 +319,9 @@ public interface IKeyRecoveryAuthority extends ISubsystem {
      * Retrieves the transport certificate.
      */
     public org.mozilla.jss.crypto.X509Certificate getTransportCert();
+
+    /**
+     * Process synchronous archival and recovery requests
+     */
+    public void processSynchronousRequest(IRequest request) throws EBaseException;
 }
diff --git a/base/common/src/com/netscape/certsrv/request/IRequestQueue.java b/base/common/src/com/netscape/certsrv/request/IRequestQueue.java
index 844f90f18f627103a7e1a5751f39702c65cc4923..a6ea608256591e15c2de2eb8d5960da8d436e7f2 100644
--- a/base/common/src/com/netscape/certsrv/request/IRequestQueue.java
+++ b/base/common/src/com/netscape/certsrv/request/IRequestQueue.java
@@ -38,6 +38,18 @@ import com.netscape.certsrv.dbs.repository.IRepository;
 public interface IRequestQueue {
 
     /**
+     * Create a new Request object and assign a request ID.
+     * See newRequest() for details.
+     *
+     * @param requestType - request type
+     * @param synchronous - is the request synchronous?
+     * @return new request
+     * @exception EBaseException failed to create new request
+     */
+    public IRequest newRequest(String requestType, boolean synchronous)
+            throws EBaseException;
+
+    /**
      * Creates a new request object. A request id is
      * assigned to it - see IRequest.getRequestId, and
      * the status is set to RequestStatus.BEGIN
diff --git a/base/common/src/com/netscape/certsrv/request/RequestId.java b/base/common/src/com/netscape/certsrv/request/RequestId.java
index 6f31a18be0c06730bdc32e8c560837cb4aa0044a..d3e956ba35163c6abd1e15e96717eb71c6981c2d 100644
--- a/base/common/src/com/netscape/certsrv/request/RequestId.java
+++ b/base/common/src/com/netscape/certsrv/request/RequestId.java
@@ -77,6 +77,10 @@ public class RequestId implements Serializable {
         value = BigInteger.valueOf(id);
     }
 
+    public RequestId(long id) {
+        value = BigInteger.valueOf(id);
+    }
+
     /**
      * Converts the RequestId into its BigInteger representation.
      * <p>
diff --git a/base/java-tools/src/com/netscape/cmstools/key/KeyArchiveCLI.java b/base/java-tools/src/com/netscape/cmstools/key/KeyArchiveCLI.java
index e9ce7f2ec48017e6f78ab923dbc0adea2011abf6..fc5c506049e4b58d3896b2dd94880b28e4999d11 100644
--- a/base/java-tools/src/com/netscape/cmstools/key/KeyArchiveCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/key/KeyArchiveCLI.java
@@ -49,6 +49,10 @@ public class KeyArchiveCLI extends CLI {
         option = new Option(null, "realm", true, "Authorization realm.");
         option.setArgName("Realm");
         options.addOption(option);
+
+        option = new Option(null, "express", false, "Express request.");
+        option.setArgName("Express request");
+        options.addOption(option);
     }
 
     public void execute(String[] args) {
@@ -89,18 +93,20 @@ public class KeyArchiveCLI extends CLI {
                 Unmarshaller unmarshaller = context.createUnmarshaller();
                 FileInputStream fis = new FileInputStream(requestFile);
                 KeyArchivalRequest req = (KeyArchivalRequest) unmarshaller.unmarshal(fis);
+                boolean express = req.isExpress();
 
                 if (req.getPKIArchiveOptions() != null) {
                     response = keyCLI.keyClient.archivePKIOptions(req.getClientKeyId(), req.getDataType(),
                             req.getKeyAlgorithm(), req.getKeySize(), Utils.base64decode(req.getPKIArchiveOptions()),
-                            req.getRealm());
+                            req.getRealm(), express);
                 } else {
                     response = keyCLI.keyClient.archiveEncryptedData(req.getClientKeyId(), req.getDataType(),
                             req.getKeyAlgorithm(), req.getKeySize(), req.getAlgorithmOID(),
                             Utils.base64decode(req.getSymmetricAlgorithmParams()),
                             Utils.base64decode(req.getWrappedPrivateData()),
                             Utils.base64decode(req.getTransWrappedSessionKey()),
-                            req.getRealm());
+                            req.getRealm(),
+                            express);
                 }
 
             } catch (JAXBException e) {
@@ -119,6 +125,7 @@ public class KeyArchiveCLI extends CLI {
             // Simple case for archiving a passphrase
             String clientKeyId = cmd.getOptionValue("clientKeyID");
             String passphrase = cmd.getOptionValue("passphrase");
+            boolean express = cmd.hasOption("express");
             if (clientKeyId == null) {
                 System.err.println("Error: Client Key Id is not specified.");
                 printHelp();
@@ -132,7 +139,7 @@ public class KeyArchiveCLI extends CLI {
             String realm = cmd.getOptionValue("realm");
 
             try {
-                response = keyCLI.keyClient.archivePassphrase(clientKeyId, passphrase, realm);
+                response = keyCLI.keyClient.archivePassphrase(clientKeyId, passphrase, realm, express);
             } catch (Exception e) {
                 System.err.println(e.getMessage());
                 if (verbose)
diff --git a/base/java-tools/src/com/netscape/cmstools/key/KeyRetrieveCLI.java b/base/java-tools/src/com/netscape/cmstools/key/KeyRetrieveCLI.java
index 92389c021ea060f3c69f8aa69fe962dd9dbbb44f..7214ceef87bc206604f5f5685e5ae7f67e7f9f6b 100644
--- a/base/java-tools/src/com/netscape/cmstools/key/KeyRetrieveCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/key/KeyRetrieveCLI.java
@@ -50,6 +50,10 @@ public class KeyRetrieveCLI extends CLI {
         option = new Option(null, "output", true, "Location to store the retrieved key information");
         option.setArgName("File path to store key information");
         options.addOption(option);
+
+        option = new Option(null, "express", false, "Express Request");
+        option.setArgName("Express Request");
+        options.addOption(option);
     }
 
     public void execute(String[] args) throws Exception {
@@ -94,6 +98,7 @@ public class KeyRetrieveCLI extends CLI {
             Unmarshaller unmarshaller = context.createUnmarshaller();
             FileInputStream fis = new FileInputStream(requestFile);
             KeyRecoveryRequest req = (KeyRecoveryRequest) unmarshaller.unmarshal(fis);
+            boolean express = req.isExpress();
 
             if (req.getKeyId() == null) {
                 System.err.println("Error: Key ID must be specified in the request file.");
@@ -102,35 +107,38 @@ public class KeyRetrieveCLI extends CLI {
 
             if (req.getCertificate() != null) {
                 keyData = keyCLI.keyClient.retrieveKeyByPKCS12(req.getKeyId(), req.getCertificate(),
-                        req.getPassphrase());
+                        req.getPassphrase(), express);
 
             } else if (req.getPassphrase() != null) {
-                keyData = keyCLI.keyClient.retrieveKeyByPassphrase(req.getKeyId(), req.getPassphrase());
+                keyData = keyCLI.keyClient.retrieveKeyByPassphrase(req.getKeyId(), req.getPassphrase(), express);
 
             } else if (req.getSessionWrappedPassphrase() != null) {
                 keyData = keyCLI.keyClient.retrieveKeyUsingWrappedPassphrase(req.getKeyId(),
                         Utils.base64decode(req.getTransWrappedSessionKey()),
                         Utils.base64decode(req.getSessionWrappedPassphrase()),
-                        Utils.base64decode(req.getNonceData()));
+                        Utils.base64decode(req.getNonceData()),
+                        express);
 
             } else if (req.getTransWrappedSessionKey() != null) {
                 keyData = keyCLI.keyClient.retrieveKey(req.getKeyId(),
-                        Utils.base64decode(req.getTransWrappedSessionKey()));
+                        Utils.base64decode(req.getTransWrappedSessionKey()),
+                        express);
 
             } else {
-                keyData = keyCLI.keyClient.retrieveKey(req.getKeyId());
+                keyData = keyCLI.keyClient.retrieveKey(req.getKeyId(), express);
             }
 
         } else {
             // Using command line options.
             String keyId = cmd.getOptionValue("keyID");
             String passphrase = cmd.getOptionValue("passphrase");
+            boolean express = cmd.hasOption("express");
 
             if (passphrase != null) {
-                keyData = keyCLI.keyClient.retrieveKeyByPassphrase(new KeyId(keyId), passphrase);
+                keyData = keyCLI.keyClient.retrieveKeyByPassphrase(new KeyId(keyId), passphrase, express);
 
             } else {
-                keyData = keyCLI.keyClient.retrieveKey(new KeyId(keyId));
+                keyData = keyCLI.keyClient.retrieveKey(new KeyId(keyId), express);
                 clientEncryption = false;
 
                 // No need to return the encrypted data since encryption
diff --git a/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java b/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
index cda3ba659b935139f23f5bae2ac6b1a41c060a8f..c1447fa9cc18d3279bf0c3107129bffa1105194d 100644
--- a/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
+++ b/base/kra/src/com/netscape/kra/KeyRecoveryAuthority.java
@@ -1252,6 +1252,25 @@ public class KeyRecoveryAuthority implements IAuthority, IKeyService, IKeyRecove
     }
 
     /**
+     * Execute synchronous request
+     * (TODO(alee): should we do this in a separate thread?
+     * @throws EBaseException
+     */
+    public void processSynchronousRequest(IRequest request) throws EBaseException {
+        SecurityDataProcessor processor = new SecurityDataProcessor(this);
+        switch(request.getRequestType()){
+            case IRequest.SECURITY_DATA_ENROLLMENT_REQUEST:
+                processor.archive(request);
+                break;
+            case IRequest.SECURITY_DATA_RECOVERY_REQUEST:
+                processor.recover(request);
+                break;
+            default:
+                throw new EBaseException("Unsupported synchronous request type: " + request.getRequestType());
+        }
+    }
+
+    /**
      * Recovers key for end-entities.
      *
      * @param creds list of credentials
diff --git a/base/kra/src/com/netscape/kra/SecurityDataProcessor.java b/base/kra/src/com/netscape/kra/SecurityDataProcessor.java
index 70e712dc691a6964851c974734a859ed95ebeff9..36da8c4ee040e627b0f3262aefa1cb99414a7bd4 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataProcessor.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataProcessor.java
@@ -81,6 +81,7 @@ public class SecurityDataProcessor {
         this.kra = kra;
         transportUnit = kra.getTransportKeyUnit();
         storageUnit = kra.getStorageKeyUnit();
+        keyRepository = kra.getKeyRepository();
     }
 
     public boolean archive(IRequest request)
@@ -247,8 +248,7 @@ public class SecurityDataProcessor {
             throw new EBaseException(CMS.getUserMessage("CMS_KRA_INVALID_STATE"));
         }
 
-        IKeyRepository storage = kra.getKeyRepository();
-        BigInteger serialNo = storage.getNextSerialNumber();
+        BigInteger serialNo = keyRepository.getNextSerialNumber();
 
         if (serialNo == null) {
             kra.log(ILogger.LL_FAILURE,
@@ -273,7 +273,7 @@ public class SecurityDataProcessor {
 
         CMS.debug("KRA adding Security Data key record " + serialNo);
 
-        storage.addKeyRecord(rec);
+        keyRepository.addKeyRecord(rec);
 
         auditArchivalRequestProcessed(auditSubjectID, ILogger.SUCCESS, requestId,
                 clientKeyId, serialNo.toString(), "None");
diff --git a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
index 5b846567983c091b69338a1fc79c52ef76721ae0..0c7b4b70e685338b31f2f5d27c819c863ef998c5 100644
--- a/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
+++ b/base/kra/src/com/netscape/kra/SecurityDataRecoveryService.java
@@ -29,7 +29,6 @@ import com.netscape.certsrv.request.IService;
  *
  * @version $Revision$, $Date$
  */
-@SuppressWarnings("deprecation")
 public class SecurityDataRecoveryService implements IService {
 
     private IKeyRecoveryAuthority kra = null;
diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java
index 103b78923ee9200bed2e0c8608168f35c311df9c..f6918509a0107b6f917e66538c49a4e30c23c978 100644
--- a/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java
+++ b/base/kra/src/org/dogtagpki/server/kra/rest/KeyRequestService.java
@@ -202,20 +202,9 @@ public class KeyRequestService extends PKIService implements KeyRequestResource
         //If data has been provided, we need at least the wrapped session key,
         //or the command is invalid.
 
-        if (data == null) {
-            throw new BadRequestException("Invalid request.");
-        }
-        if (data.getCertificate() == null &&
-            data.getTransWrappedSessionKey() == null &&
-            data.getSessionWrappedPassphrase() != null) {
-            throw new BadRequestException("No wrapped session key.");
-        }
         KeyRequestDAO dao = new KeyRequestDAO();
         KeyRequestResponse response;
         try {
-            if (getRequestor() == null) {
-                throw new UnauthorizedException("Recovery must be initiated by an agent");
-            }
             response = (data.getCertificate() != null)?
                     dao.submitAsyncKeyRecoveryRequest(data, uriInfo, getRequestor(), getAuthToken()):
                     dao.submitRequest(data, uriInfo, getRequestor(), getAuthToken());
diff --git a/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java b/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java
index 74b58b8a2e3c036925e12114d578a4069b136c74..c478bb7ab14009b1e1fe0d96538bb755b3ffb9e7 100644
--- a/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java
+++ b/base/kra/src/org/dogtagpki/server/kra/rest/KeyService.java
@@ -62,7 +62,6 @@ import com.netscape.certsrv.key.KeyInfo;
 import com.netscape.certsrv.key.KeyInfoCollection;
 import com.netscape.certsrv.key.KeyNotFoundException;
 import com.netscape.certsrv.key.KeyRecoveryRequest;
-import com.netscape.certsrv.key.KeyRequestInfo;
 import com.netscape.certsrv.key.KeyResource;
 import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
 import com.netscape.certsrv.kra.IKeyService;
@@ -102,12 +101,18 @@ public class KeyService extends PKIService implements KeyResource {
 
     public static final int DEFAULT_MAXRESULTS = 100;
     public static final int DEFAULT_MAXTIME = 10;
+    public static final String ATTR_SERIALNO = "serialNumber";
 
     private IKeyRepository repo;
     private IKeyRecoveryAuthority kra;
     private IRequestQueue queue;
     private IKeyService service;
 
+    //parameters for auditing
+    private RequestId requestId;
+    private KeyId keyId;
+    private String auditInfo;
+
     public KeyService() {
         kra = ( IKeyRecoveryAuthority ) CMS.getSubsystem( "kra" );
         repo = kra.getKeyRepository();
@@ -122,114 +127,124 @@ public class KeyService extends PKIService implements KeyResource {
      */
     @Override
     public Response retrieveKey(KeyRecoveryRequest data) {
-
-        CMS.debug("KeyService.retrieveKey()");
-        String auditInfo = "KeyService.retrieveKey";
+        auditInfo = "KeyService.retrieveKey";
+        CMS.debug(auditInfo);
 
         if (data == null) {
-            String message = "Missing key recovery request";
-            CMS.debug(message);
-            auditRetrieveKey(ILogger.FAILURE, "None", "None", auditInfo + ";" + message);
-            throw new BadRequestException(message);
+            errorOut("Missing key recovery request", new BadRequestException("Missing key Recovery Request"));
         }
 
-        RequestId requestID = data.getRequestId();
-        CMS.debug("KeyService: request ID: " + requestID);
+        boolean express = data.isExpress();
+        keyId = data.getKeyId();
+        if (keyId == null) {
+            errorOut("Missing key ID", new BadRequestException("Missing key Recovery Request"));
+        }
 
-        if (requestID != null)
-            auditInfo = auditInfo + ": requestID=" + requestID.toString();
+        auditInfo += ";keyID=" + keyId.toString() + ";express="+ Boolean.toString(express);
+        CMS.debug(auditInfo);
 
-        KeyId keyId = data.getKeyId();
-        CMS.debug("KeyService: key ID: " + keyId);
-        if (keyId != null)
-            auditInfo = auditInfo + "; keyID=" + keyId.toString();
+        // get request
+        IRequest request = null;
+        requestId = data.getRequestId();
+        if (!express) {
+            if (requestId == null) {
+                errorOut("Missing requestID", new BadRequestException("missing request ID"));
+            }
 
-        IRequest request;
-        try {
-            request = queue.findRequest(requestID);
+            CMS.debug("KeyService: request ID: " + requestId);
+            auditInfo += ";requestID=" + requestId.toString();
 
-        } catch (EBaseException e) {
-            CMS.debug(e);
-            auditRetrieveKey(ILogger.FAILURE, requestID, null, auditInfo + ";" + e.getMessage());
-            throw new PKIException(e.getMessage(), e);
+            try {
+                request = queue.findRequest(requestId);
+            } catch (EBaseException e) {
+                errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
+            }
+        } else {
+            // express request
+            KeyRequestDAO reqDAO = new KeyRequestDAO();
+            try {
+                request = reqDAO.createRecoveryRequest(data, uriInfo, getRequestor(), getAuthToken());
+            } catch (EBaseException e) {
+                errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
+            }
+            requestId = request.getRequestId();
+            auditInfo += ";requestID=" + requestId.toString();
+            data.setRequestId(requestId);
         }
 
         String type = request.getRequestType();
         CMS.debug("KeyService: request type: " + type);
-        auditInfo = auditInfo + "; request type:" + type;
+        auditInfo += ";request type:" + type;
 
-        KeyData keyData;
+        KeyData keyData = null;
         try {
-            if (IRequest.KEYRECOVERY_REQUEST.equals(type)) {
-                keyData = recoverKey(data);
-
-            } else {
-                keyId = validateRequest(data);
-                keyData = getKey(keyId, data);
+            switch(type) {
+                case IRequest.KEYRECOVERY_REQUEST:
+                    keyData = recoverKey(data);
+                    break;
+                case IRequest.SECURITY_DATA_RECOVERY_REQUEST:
+                    validateRequest(data, request);
+                    keyData = getKey(keyId, request, data);
+                    break;
+                default:
+                    errorOut("Invalid request type: " + type,
+                             new BadRequestException("Invalid request type: " + type));
             }
-
         } catch (Exception e) {
-            CMS.debug(e);
-            auditRetrieveKey(ILogger.FAILURE, requestID, keyId, auditInfo + ";" + e.getMessage());
-            throw new PKIException(e.getMessage(), e);
+            errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
         }
 
         if (keyData == null) {
-            CMS.debug("KeyService: No key record");
-            auditRetrieveKey(ILogger.FAILURE, requestID, keyId, auditInfo + "; No key record");
-            throw new HTTPGoneException("No key record.");
+            errorOut("No key record", new HTTPGoneException("No key record."));
         }
 
         CMS.debug("KeyService: key retrieved");
-
-        auditRetrieveKey(ILogger.SUCCESS, requestID, keyId, auditInfo);
-
+        auditRetrieveKey(ILogger.SUCCESS);
         return createOKResponse(keyData);
     }
 
     // retrieval - used to test integration with a browser
     @Override
     public Response retrieveKey(MultivaluedMap<String, String> form) {
-        String method = "KeyService.retrieveKey with form: ";
-        CMS.debug(method + "begins.");
+        CMS.debug("KeyService.retrieveKey with form: begins.");
         KeyRecoveryRequest data = new KeyRecoveryRequest(form);
         return retrieveKey(data);
     }
 
-    public KeyData getKey(KeyId keyId, KeyRecoveryRequest data) throws EBaseException {
-        String method = "KeyService.getKey: ";
-        String auditInfo = null;
+    public KeyData getKey(KeyId keyId, IRequest request, KeyRecoveryRequest data) throws EBaseException {
+        String method = "KeyService.getKey:";
+        auditInfo = method;
         KeyData keyData;
         CMS.debug(method + "begins.");
-        RequestId rId = data.getRequestId();
 
-        String transWrappedSessionKey;
-        String sessionWrappedPassphrase;
-
-        IRequest request = queue.findRequest(rId);
+        if (data == null) {
+            CMS.debug(method + "KeyRecoveryRequest is null");
+            return null;
+        }
 
         if (request == null) {
             CMS.debug(method + "request null");
             return null;
         }
 
-     // get wrapped key
+        // get wrapped key
         IKeyRecord rec = repo.readKeyRecord(keyId.toBigInteger());
         if (rec == null) {
             CMS.debug(method + "key record null");
-
             return null;
         }
 
-        Hashtable<String, Object> requestParams = kra.getVolatileRequest(
-                request.getRequestId());
+        // get data from the KeyRecoveryRequest
+        boolean express = data.isExpress();
+        String transWrappedSessionKey   = data.getTransWrappedSessionKey();
+        String sessionWrappedPassphrase = data.getSessionWrappedPassphrase();
 
+        // get data from IRequest
+        Hashtable<String, Object> requestParams = kra.getVolatileRequest(request.getRequestId());
         if(requestParams == null) {
-            auditInfo = method + "Can't obtain Volatile requestParams in getKey!";
-            CMS.debug(auditInfo);
-            throw new EBaseException(auditInfo);
+            errorOut("Can't obtain Volatile requestParams in getKey!",
+                     new PKIException("Can't obtain Volatile requestParams in getKey!"));
         }
-
         String sessWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_SESS_WRAPPED_DATA);
         String passWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA);
         String nonceData = (String) requestParams.get(IRequest.SECURITY_DATA_IV_STRING_OUT);
@@ -239,23 +254,12 @@ public class KeyService extends PKIService implements KeyResource {
             //package, either session key wrapped or pass wrapped, into the request.
             //Request already has been processed.
             keyData = new KeyData();
-
         } else {
             // The request has not yet been processed, let's see if the RecoveryRequestData contains
             // the info now needed to process the recovery request.
 
-            transWrappedSessionKey   = data.getTransWrappedSessionKey();
-            sessionWrappedPassphrase = data.getSessionWrappedPassphrase();
             nonceData = data.getNonceData();
 
-            if (transWrappedSessionKey == null) {
-                //There must be at least a transWrappedSessionKey input provided.
-                //The command AND the request have provided insufficient data, end of the line.
-                auditInfo = method + "Can't retrieve key, insufficient input data!";
-                CMS.debug(auditInfo);
-                throw new EBaseException(auditInfo);
-            }
-
             if (sessionWrappedPassphrase != null) {
                 requestParams.put(IRequest.SECURITY_DATA_SESS_PASS_PHRASE, sessionWrappedPassphrase);
             }
@@ -269,22 +273,23 @@ public class KeyService extends PKIService implements KeyResource {
             }
 
             try {
-                // Has to be in this state or it won't go anywhere.
-                request.setRequestStatus(RequestStatus.BEGIN);
-                queue.processRequest(request);
+                if (!express) {
+                    // Has to be in this state or it won't go anywhere.
+                    request.setRequestStatus(RequestStatus.BEGIN);
+                    queue.processRequest(request);
+                } else {
+                    kra.processSynchronousRequest(request);
+                }
             } catch (EBaseException e) {
-                auditInfo = method + e.getMessage();
                 kra.destroyVolatileRequest(request.getRequestId());
-                throw new EBaseException(auditInfo);
+                errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
             }
 
-            nonceData = null;
+            // get the results of the operations
             keyData = new KeyData();
-
             sessWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_SESS_WRAPPED_DATA);
             passWrappedKeyData = (String) requestParams.get(IRequest.SECURITY_DATA_PASS_WRAPPED_DATA);
             nonceData = (String) requestParams.get(IRequest.SECURITY_DATA_IV_STRING_OUT);
-
         }
 
         if (sessWrappedKeyData != null) {
@@ -310,93 +315,44 @@ public class KeyService extends PKIService implements KeyResource {
 
         kra.destroyVolatileRequest(request.getRequestId());
 
-        queue.markAsServiced(request);
+        if (!express) {
+            queue.markAsServiced(request);
+        }
 
         return keyData;
     }
 
-    private KeyId validateRequest(KeyRecoveryRequest data) {
+    private void validateRequest(KeyRecoveryRequest data, IRequest request) {
         String method = "KeyService.validateRequest: ";
         CMS.debug(method + "begins.");
-        String logMessage = null;
-
-        // confirm request exists
-        RequestId reqId = data.getRequestId();
-        if (reqId == null) {
-            // log error
-            logMessage = "Request id not found.";
-            CMS.debug(logMessage);
-            throw new BadRequestException(logMessage);
-        }
+        boolean express = data.isExpress();
 
         // confirm that at least one wrapping method exists
         // There must be at least the wrapped session key method.
         if ((data.getTransWrappedSessionKey() == null)) {
-            // log error
-            logMessage = "No wrapping method found.";
-            CMS.debug(logMessage);
-
-            throw new BadRequestException(logMessage);
-        }
-
-        KeyRequestDAO reqDAO = new KeyRequestDAO();
-        KeyRequestInfo reqInfo;
-        try {
-            reqInfo = reqDAO.getRequest(reqId, uriInfo, getAuthToken());
-        } catch (EBaseException e1) {
-            // failed to get request
-            logMessage = "failed to get request";
-            CMS.debug(logMessage);
-
-            e1.printStackTrace();
-            throw new PKIException(logMessage + e1.getMessage(), e1);
-        }
-        if (reqInfo == null) {
-            // request not found
-            logMessage = "No request information available.";
-            CMS.debug(logMessage);
-
-            throw new HTTPGoneException(logMessage);
+            errorOut("No wrapping method found.", new BadRequestException("No wrapping method found."));
         }
 
-        //confirm request is of the right type
-        String type = reqInfo.getRequestType();
-        if (!type.equals(IRequest.SECURITY_DATA_RECOVERY_REQUEST)) {
-            // log error
-            logMessage = "Invalid request type";
-            CMS.debug(logMessage);
-            throw new BadRequestException(logMessage);
+        // confirm that the keyIDs match
+        String keyID = request.getExtDataInString(ATTR_SERIALNO);
+        if (!data.getKeyId().toString().contentEquals(keyID)) {
+            errorOut("Key IDs do not match.", new UnauthorizedException("Key IDs do not match"));
         }
 
         //confirm that retriever is originator of request, else throw 401
         String retriever = servletRequest.getUserPrincipal().getName();
-        IRequest request;
-        try {
-            request = queue.findRequest(reqId);
-        } catch (EBaseException e) {
-            e.printStackTrace();
-            logMessage = e.getMessage();
-            CMS.debug(logMessage);
-
-            throw new PKIException(logMessage, e);
-        }
         String originator = request.getExtDataInString(IRequest.ATTR_REQUEST_OWNER);
         if (! originator.equals(retriever)) {
-            logMessage = "Data for recovery requests can only be retrieved by the originators of the request";
-            CMS.debug(logMessage);
-            throw new UnauthorizedException(logMessage);
+            errorOut("Data can only be retrieved by the originators of the request",
+                     new UnauthorizedException("Data can only be retrieved by the originators of the request"));
         }
 
         // confirm request is in approved state
-        RequestStatus status = reqInfo.getRequestStatus();
-        if (!status.equals(RequestStatus.APPROVED)) {
-            // log error
-            logMessage = "Unauthorized request.  Recovery request not approved.";
-            CMS.debug(logMessage);
-            throw new UnauthorizedException(logMessage);
+        RequestStatus status = request.getRequestStatus();
+        if (!express && !status.equals(RequestStatus.APPROVED)) {
+            errorOut("Recovery request not approved.",
+                     new UnauthorizedException("Recovery request not approved."));
         }
-
-        return reqInfo.getKeyId();
     }
 
     /**
@@ -405,16 +361,14 @@ public class KeyService extends PKIService implements KeyResource {
     @Override
     public Response listKeys(String clientKeyID, String status, Integer maxResults, Integer maxTime,
             Integer start, Integer size, String realm) {
-        String method = "KeyService.listKeys: ";
-        CMS.debug(method + "begins.");
-
+        CMS.debug("KeyService.listKeys: begins.");
         return createOKResponse(listKeyInfos(clientKeyID, status, maxResults, maxTime, start, size, realm));
     }
 
     public KeyInfoCollection listKeyInfos(String clientKeyID, String status, Integer maxResults, Integer maxTime,
             Integer start, Integer size, String realm) {
         String method = "KeyService.listKeyInfos: ";
-        String auditInfo = "KeyService.listKeyInfos; status =" + status;
+        auditInfo = "KeyService.listKeyInfos; status =" + status;
         CMS.debug(method + "begins.");
 
         start = start == null ? 0 : start;
@@ -444,7 +398,7 @@ public class KeyService extends PKIService implements KeyResource {
         try {
             Enumeration<IKeyRecord> e = repo.searchKeys(filter, maxResults, maxTime);
             if (e == null) {
-                auditRetrieveKey(ILogger.SUCCESS, null, clientKeyID, auditInfo);
+                auditRetrieveKey(ILogger.SUCCESS);
                 return infos;
             }
 
@@ -475,12 +429,9 @@ public class KeyService extends PKIService implements KeyResource {
             }
 
         } catch (EBaseException e) {
-            auditRetrieveKey(ILogger.FAILURE, null, clientKeyID, e.getMessage() + auditInfo);
-
-            e.printStackTrace();
-            throw new PKIException(e.getMessage(), e);
+            errorOut(e.getMessage(), new PKIException(e.getMessage(), e));
         }
-        auditRetrieveKey(ILogger.SUCCESS, null, clientKeyID, auditInfo);
+        auditRetrieveKey(ILogger.SUCCESS);
 
         return infos;
     }
@@ -488,7 +439,7 @@ public class KeyService extends PKIService implements KeyResource {
     @Override
     public Response getActiveKeyInfo(String clientKeyID) {
         String method = "KeyService.getActiveKeyInfo: ";
-        String auditInfo = "KeyService.getActiveKeyInfo";
+        auditInfo = "KeyService.getActiveKeyInfo";
         CMS.debug(method + "begins.");
 
         KeyInfoCollection infos = listKeyInfos(
@@ -517,15 +468,13 @@ public class KeyService extends PKIService implements KeyResource {
                     throw new PKIException(e.toString(), e);
                 }
 
-                auditRetrieveKey(ILogger.SUCCESS, null, clientKeyID, auditInfo);
+                auditRetrieveKey(ILogger.SUCCESS);
 
                 return createOKResponse(info);
             }
         }
-        String message = "Key not found.";
-        auditRetrieveKey(ILogger.FAILURE, null, clientKeyID, message + auditInfo);
-
-        throw new ResourceNotFoundException(auditInfo + ":" + message);
+        errorOut("Key not found.", new ResourceNotFoundException("Key not found"));
+        return null;  //needed to keep compiler happy
     }
 
     public KeyInfo createKeyDataInfo(IKeyRecord rec, boolean getPublicKey) throws EBaseException {
@@ -590,23 +539,30 @@ public class KeyService extends PKIService implements KeyResource {
         return filter;
     }
 
-    public void auditRetrieveKey(String status, RequestId requestID, KeyId keyID, String reason) {
-        auditRetrieveKey(status, requestID != null ? requestID.toString(): "null",
-                keyID != null ? keyID.toString(): "null", reason);
-    }
-
-    public void auditRetrieveKey(String status, String requestID, String keyID, String reason) {
+    public void auditRetrieveKey(String status, String reason) {
         String msg = CMS.getLogMessage(
                 LOGGING_SIGNED_AUDIT_SECURITY_DATA_RETRIEVE_KEY,
                 servletRequest.getUserPrincipal().getName(),
                 status,
-                requestID,
-                keyID,
-                reason);
+                requestId != null ? requestId.toString(): "null",
+                keyId != null ? keyId.toString(): "null",
+                (reason != null) ? auditInfo + ";" + reason : auditInfo
+        );
         auditor.log(msg);
     }
 
-    public void auditKeyStatusChange(String status, String keyID, String oldKeyStatus, String newKeyStatus, String info) {
+    public void auditRetrieveKey(String status) {
+        auditRetrieveKey(status, null);
+    }
+
+    public void errorOut(String message, RuntimeException exception) {
+        CMS.debug(message);
+        auditRetrieveKey(ILogger.FAILURE, message);
+        throw exception;
+    }
+
+    public void auditKeyStatusChange(String status, String keyID, String oldKeyStatus,
+            String newKeyStatus, String info) {
         String msg = CMS.getLogMessage(
                 LOGGING_SIGNED_AUDIT_KEY_STATUS_CHANGE,
                 servletRequest.getUserPrincipal().getName(),
@@ -625,7 +581,7 @@ public class KeyService extends PKIService implements KeyResource {
      */
     private KeyData recoverKey(KeyRecoveryRequest data) throws UnauthorizedException, HTTPGoneException {
         String method = "KeyService.recoverKey: ";
-        String auditInfo = "KeyService.recoverKey";
+        auditInfo = "KeyService.recoverKey";
         CMS.debug(method + "begins.");
 
         // confirm request exists
@@ -637,15 +593,13 @@ public class KeyService extends PKIService implements KeyResource {
         } catch (EBaseException e) {
         }
         if (request == null) {
-            auditInfo = method + "No request record.";
-            throw new HTTPGoneException(auditInfo);
+            errorOut("No request record", new HTTPGoneException("No request record"));
         }
         String type = request.getRequestType();
         RequestStatus status = request.getRequestStatus();
         if (!IRequest.KEYRECOVERY_REQUEST.equals(type) ||
             !status.equals(RequestStatus.APPROVED)) {
-            auditInfo = method + "Unauthorized request.";
-            throw new UnauthorizedException(auditInfo);
+            errorOut("Request not approved", new UnauthorizedException("Request not approved"));
         }
 
         String passphrase = data.getPassphrase();
@@ -655,8 +609,8 @@ public class KeyService extends PKIService implements KeyResource {
         } catch (EBaseException e) {
         }
         if (pkcs12 == null) {
-            auditInfo = method + "pkcs12 null; Key not recovered.";
-            throw new HTTPGoneException(auditInfo);
+            errorOut("pkcs12 null; Key not recovered.",
+                     new HTTPGoneException("pkcs12 null; Key not recovered"));
         }
         String pkcs12base64encoded = Utils.base64encode(pkcs12);
 
@@ -675,7 +629,7 @@ public class KeyService extends PKIService implements KeyResource {
     @Override
     public Response getKeyInfo(KeyId keyId) {
         String method = "KeyService.getKeyInfo: ";
-        String auditInfo = "KeyService.getKeyInfo";
+        auditInfo = "KeyService.getKeyInfo";
         CMS.debug(method + "begins.");
 
         IKeyRecord rec = null;
@@ -683,24 +637,18 @@ public class KeyService extends PKIService implements KeyResource {
             rec = repo.readKeyRecord(keyId.toBigInteger());
             authz.checkRealm(rec.getRealm(), getAuthToken(), rec.getOwnerName(), "certServer.kra.key", "read");
             KeyInfo info = createKeyDataInfo(rec, true);
-            auditRetrieveKey(ILogger.SUCCESS, null, keyId, auditInfo);
+            auditRetrieveKey(ILogger.SUCCESS);
 
             return createOKResponse(info);
         } catch (EAuthzAccessDenied e) {
-            auditInfo = method + "Unauthorized access for key record";
-            auditRetrieveKey(ILogger.FAILURE, null, keyId, auditInfo);
-            throw new UnauthorizedException(auditInfo, e);
+            errorOut("Unauthorized access for key record",
+                      new UnauthorizedException("Unauthorized access for key record", e));
         } catch (EDBRecordNotFoundException e) {
-            auditInfo = method + e.getMessage();
-            auditRetrieveKey(ILogger.FAILURE, null, keyId, auditInfo);
-            throw new KeyNotFoundException(keyId, "key not found", e);
+            errorOut(e.getMessage(), new KeyNotFoundException(keyId, "key not found", e));
         } catch (Exception e) {
-            auditInfo = method + "Unable to retrieve key record: " + e.getMessage();
-            auditRetrieveKey(ILogger.FAILURE, null, keyId, auditInfo);
-            CMS.debug(auditInfo);
-            e.printStackTrace();
-            throw new PKIException(e.getMessage(), e);
+            errorOut("Unable to retrieve key record: " + e.getMessage(), new PKIException(e.getMessage(), e));
         }
+        return null; // keep compiler happy
     }
 
     private IAuthToken getAuthToken() {
@@ -714,7 +662,7 @@ public class KeyService extends PKIService implements KeyResource {
     public Response modifyKeyStatus(KeyId keyId, String status) {
         String method = "KeyService.modifyKeyStatus: ";
         //TODO: what was the original status?  find it and record that in Info as well
-        String auditInfo = "KeyService.modifyKeyStatus";
+        auditInfo = "KeyService.modifyKeyStatus";
 
         CMS.debug(method + "begins.");
         IKeyRecord rec = null;
@@ -748,5 +696,8 @@ public class KeyService extends PKIService implements KeyResource {
         }
     }
 
+    private String getRequestor() {
+        return servletRequest.getUserPrincipal().getName();
+    }
 
 }
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 8238c10f13a5a4eaceeb10d97f520c1cd1727e88..84d89ac51ca3b55d54a5b52c85714c25908e78d9 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
@@ -202,7 +202,7 @@ public class KeyRequestDAO extends CMSRequestDAO {
             throw new BadRequestException("Can not archive already active existing key!");
         }
 
-        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_ENROLLMENT_REQUEST);
+        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_ENROLLMENT_REQUEST, data.isExpress());
 
         if (pkiArchiveOptions != null) {
             request.setExtData(REQUEST_ARCHIVE_OPTIONS, pkiArchiveOptions);
@@ -227,34 +227,36 @@ public class KeyRequestDAO extends CMSRequestDAO {
             request.setRealm(realm);
         }
 
-        queue.processRequest(request);
-
-        queue.markAsServiced(request);
+        if (!data.isExpress()) {
+            queue.processRequest(request);
+            queue.markAsServiced(request);
+        } else {
+            kra.processSynchronousRequest(request);
+        }
 
         return createKeyRequestResponse(request, uriInfo);
     }
 
-    /**
-     * Submits a key recovery request.
-     *
-     * @param data
-     * @param uriInfo
-     * @param requestor
-     * @param authToken
-     * @return info on the recovery request created
-     * @throws EBaseException
-     */
-    public KeyRequestResponse submitRequest(KeyRecoveryRequest data, UriInfo uriInfo, String requestor,
-            IAuthToken authToken)
-            throws EBaseException {
-        // set data using request.setExtData(field, data)
+    public IRequest createRecoveryRequest(KeyRecoveryRequest data, UriInfo uriInfo, String requestor,
+            IAuthToken authToken) throws EBaseException{
+        if (data == null) {
+            throw new BadRequestException("Invalid request.");
+        }
+
+        /*if (data.getCertificate() == null &&
+            data.getTransWrappedSessionKey() == null &&
+            data.getSessionWrappedPassphrase() != null) {
+            throw new BadRequestException("No wrapped session key.");
+        }*/
+
+        if (requestor == null) {
+            throw new UnauthorizedException("Recovery must be initiated by an agent");
+        }
 
         String wrappedSessionKeyStr = data.getTransWrappedSessionKey();
         String wrappedPassPhraseStr = data.getSessionWrappedPassphrase();
         String nonceDataStr = data.getNonceData();
 
-        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_RECOVERY_REQUEST);
-
         KeyId keyId = data.getKeyId();
         IKeyRecord rec = null;
         try {
@@ -271,12 +273,18 @@ public class KeyRequestDAO extends CMSRequestDAO {
             throw new UnauthorizedException("Agent not authorized by realm", e);
         }
 
+        IRequest request = queue.newRequest(IRequest.SECURITY_DATA_RECOVERY_REQUEST, data.isExpress());
+
+        if (rec.getRealm() != null) {
+            request.setRealm(rec.getRealm());
+        }
+
         Hashtable<String, Object> requestParams;
 
         requestParams = ((IKeyRecoveryAuthority) authority).createVolatileRequest(request.getRequestId());
 
         if (requestParams == null) {
-            throw new EBaseException("Can not create Volatile params in submitRequest!");
+            throw new EBaseException("Can not create Volatile params in createRecoveryRequest!");
         }
 
         CMS.debug("Create volatile  params for recovery request. " + requestParams);
@@ -298,10 +306,23 @@ public class KeyRequestDAO extends CMSRequestDAO {
         request.setExtData(IRequest.ATTR_REQUEST_OWNER, requestor);
         request.setExtData(IRequest.ATTR_APPROVE_AGENTS, requestor);
 
-        if (rec.getRealm() != null) {
-            request.setRealm(rec.getRealm());
-        }
+        return request;
+    }
 
+    /**
+     * Submits a key recovery request.
+     *
+     * @param data
+     * @param uriInfo
+     * @param requestor
+     * @param authToken
+     * @return info on the recovery request created
+     * @throws EBaseException
+     */
+    public KeyRequestResponse submitRequest(KeyRecoveryRequest data, UriInfo uriInfo, String requestor,
+            IAuthToken authToken)
+            throws EBaseException {
+        IRequest request = createRecoveryRequest(data, uriInfo, requestor, authToken);
         queue.processRequest(request);
 
         return createKeyRequestResponse(request, uriInfo);
diff --git a/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java b/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java
index fae48f2f7fd275f107fa0c10407ec0c9b9dd1b8b..3dff2a6a65b4b3eb93b2d2c3a3660f002d858f62 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/request/ARequestQueue.java
@@ -105,6 +105,11 @@ public abstract class ARequestQueue
             throws EBaseException;
 
     /**
+     * Create a new synchronous request ID
+     */
+    protected abstract RequestId newSynchronousRequestId();
+
+    /**
      * Read a request from the persistant store. (abstract)
      * <p>
      * This function is called to create the in-memory version of a request object.
@@ -243,18 +248,29 @@ public abstract class ARequestQueue
         return r;
     }
 
+    public IRequest newRequest(String requestType) throws EBaseException {
+        return newRequest(requestType, false);
+    }
+
     /**
      * Implements IRequestQueue.newRequest
      * <p>
      *
      * @see IRequestQueue#newRequest
      */
-    public IRequest newRequest(String requestType)
+    public IRequest newRequest(String requestType, boolean synchronous)
             throws EBaseException {
         if (requestType == null) {
             throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_REQUEST_TYPE", "null"));
         }
-        RequestId rId = newRequestId();
+
+        RequestId rId = null;
+        if(! synchronous) {
+            rId = newRequestId();
+        } else {
+            rId = newSynchronousRequestId();
+        }
+
         IRequest r = createRequest(rId, requestType);
 
         // Commented out the lock call because unlock is never called.
diff --git a/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java b/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java
index 6159a2ed50ceb8882c1a3932fbe9a8524d55ca99..d6d593955bc01d1daf5cefb2bdd1e50c463dbe19 100644
--- a/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java
+++ b/base/server/cmscore/src/com/netscape/cmscore/request/RequestQueue.java
@@ -18,6 +18,7 @@
 package com.netscape.cmscore.request;
 
 import java.math.BigInteger;
+import java.security.SecureRandom;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.Hashtable;
@@ -46,6 +47,7 @@ import com.netscape.cmscore.util.Debug;
 public class RequestQueue
         extends ARequestQueue
         implements IRequestMod {
+
     // ARequestQueue.newRequestId
     protected RequestId newRequestId()
             throws EBaseException {
@@ -57,6 +59,12 @@ public class RequestQueue
         return rid;
     }
 
+    protected RequestId newSynchronousRequestId() {
+        long id = System.currentTimeMillis() * 10000 + new SecureRandom().nextInt(10000);
+        RequestId rid = new RequestId(id);
+        return rid;
+    }
+
     protected IRequest readRequest(RequestId id) {
         RequestRecord record;
 
-- 
2.7.4

