>From f56c7f68c1357556c6baa5cb5472fcb2f265fda2 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Fri, 4 Apr 2014 16:25:37 -0700
Subject: [PATCH] trac ticket #888 part2 CA/KRA functions - TPS rewrite:
 provide remote authority functions

---
 .../com/netscape/cms/servlet/cert/DoRevokeTPS.java |  23 +-
 .../netscape/cms/servlet/cert/DoUnrevokeTPS.java   |  11 +-
 .../servlet/connector/GenerateKeyPairServlet.java  |  26 +-
 .../servlet/connector/TokenKeyRecoveryServlet.java |  20 +-
 .../dogtagpki/server/connector/IRemoteRequest.java |  52 +-
 base/tps-tomcat/shared/conf/CS.cfg.in              |   2 +-
 .../server/tps/cms/CAEnrollCertResponse.java       |  52 ++
 .../server/tps/cms/CARemoteRequestHandler.java     | 672 +++++++++++++++++++++
 .../server/tps/cms/CARenewCertResponse.java        |  52 ++
 .../server/tps/cms/CARevokeCertResponse.java       |  39 ++
 .../server/tps/cms/ConnectionManager.java          |  56 +-
 .../server/tps/cms/KRARecoverKeyResponse.java      |  51 ++
 .../server/tps/cms/KRARemoteRequestHandler.java    | 303 ++++++++++
 .../tps/cms/KRAServerSideKeyGenResponse.java       |  51 ++
 .../dogtagpki/server/tps/cms/RemoteResponse.java   |  40 ++
 .../tps/cms/TKSComputeRandomDataResponse.java      |   2 +-
 .../tps/cms/TKSComputeSessionKeyResponse.java      |   2 +-
 .../tps/cms/TKSCreateKeySetDataResponse.java       |   2 +-
 .../server/tps/cms/TKSEncryptDataResponse.java     |   2 +-
 .../server/tps/cms/TKSRemoteRequestHandler.java    |  55 +-
 20 files changed, 1438 insertions(+), 75 deletions(-)
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteResponse.java

diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/DoRevokeTPS.java b/base/server/cms/src/com/netscape/cms/servlet/cert/DoRevokeTPS.java
index 63806e6..b164671 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/DoRevokeTPS.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/DoRevokeTPS.java
@@ -63,6 +63,8 @@ import com.netscape.cms.servlet.common.CMSTemplate;
 import com.netscape.cms.servlet.common.CMSTemplateParams;
 import com.netscape.cms.servlet.common.ECMSGWException;
 
+import org.dogtagpki.server.connector.IRemoteRequest;
+
 /**
  * Revoke a Certificate
  *
@@ -184,24 +186,24 @@ public class DoRevokeTPS extends CMSServlet {
         CMSTemplateParams argSet = new CMSTemplateParams(header, ctx);
 
         try {
-            if (req.getParameter("revocationReason") != null) {
+            if (req.getParameter(IRemoteRequest.CA_REVOCATION_REASON) != null) {
                 reason = Integer.parseInt(req.getParameter(
-                                "revocationReason"));
+                                IRemoteRequest.CA_REVOCATION_REASON));
             }
-            if (req.getParameter("totalRecordCount") != null) {
+            if (req.getParameter(IRemoteRequest.CA_REVOKE_COUNT) != null) {
                 totalRecordCount = Integer.parseInt(req.getParameter(
-                                "totalRecordCount"));
+                                IRemoteRequest.CA_REVOKE_COUNT));
             }
-            if (req.getParameter("invalidityDate") != null) {
+            if (req.getParameter(IRemoteRequest.CA_REVOKE_INVALID_DATE) != null) {
                 long l = Long.parseLong(req.getParameter(
-                            "invalidityDate"));
+                            IRemoteRequest.CA_REVOKE_INVALID_DATE));
 
                 if (l > 0) {
                     invalidityDate = new Date(l);
                 }
             }
-            revokeAll = req.getParameter("revokeAll");
-            String comments = req.getParameter(IRequest.REQUESTOR_COMMENTS);
+            revokeAll = req.getParameter(IRemoteRequest.CA_REVOKE_ALL);
+            String comments = req.getParameter(IRemoteRequest.CA_REVOKE_REQUESTER_COMMENTS);
 
             //for audit log.
             String initiative = null;
@@ -261,7 +263,7 @@ public class DoRevokeTPS extends CMSServlet {
                 errorString = "error=" + error.toString();
             }
 
-            String pp = o_status + "\n" + errorString;
+            String pp = o_status + "&" + errorString;
             byte[] b = pp.getBytes();
             resp.setContentType("text/html");
             resp.setContentLength(b.length);
@@ -858,7 +860,8 @@ public class DoRevokeTPS extends CMSServlet {
         String requesterID = null;
 
         // Obtain the requesterID
-        requesterID = req.getParameter("requestId");
+        //TODO: should use tps subsystem user id
+        requesterID = req.getParameter(IRemoteRequest.CA_REVOKE_REQUESTER_ID);
 
         if (requesterID != null) {
             requesterID = requesterID.trim();
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/DoUnrevokeTPS.java b/base/server/cms/src/com/netscape/cms/servlet/cert/DoUnrevokeTPS.java
index 0437a2e..859e5dc 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/DoUnrevokeTPS.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/DoUnrevokeTPS.java
@@ -53,6 +53,8 @@ import com.netscape.cms.servlet.base.CMSServlet;
 import com.netscape.cms.servlet.common.CMSRequest;
 import com.netscape.cms.servlet.common.ECMSGWException;
 
+import org.dogtagpki.server.connector.IRemoteRequest;
+
 /**
  * 'Unrevoke' a certificate. (For certificates that are on-hold only,
  * take them off-hold)
@@ -169,7 +171,7 @@ public class DoUnrevokeTPS extends CMSServlet {
                 cmsReq.setStatus(ICMSRequest.UNAUTHORIZED);
                 o_status = "status=3";
                 errorString = "error=unauthorized";
-                String pp = o_status + "\n" + errorString;
+                String pp = o_status + "&" + errorString;
                 byte[] b = pp.getBytes();
                 resp.setContentType("text/html");
                 resp.setContentLength(b.length);
@@ -197,7 +199,7 @@ public class DoUnrevokeTPS extends CMSServlet {
                 errorString = "error=" + error.toString();
             }
 
-            String pp = o_status + "\n" + errorString;
+            String pp = o_status + "&" + errorString;
             byte[] b = pp.getBytes();
             resp.setContentType("text/html");
             resp.setContentLength(b.length);
@@ -516,7 +518,7 @@ public class DoUnrevokeTPS extends CMSServlet {
 
     private BigInteger[] getSerialNumbers(HttpServletRequest req)
             throws NumberFormatException {
-        String serialNumString = req.getParameter("serialNumber");
+        String serialNumString = req.getParameter(IRemoteRequest.CA_UNREVOKE_SERIAL);
 
         StringTokenizer snList = new StringTokenizer(serialNumString, " ");
         Vector<BigInteger> biList = new Vector<BigInteger>();
@@ -569,7 +571,8 @@ public class DoUnrevokeTPS extends CMSServlet {
         String requesterID = null;
 
         // Obtain the requesterID
-        requesterID = req.getParameter("requestId");
+        // TODO: should use tps subsystem user id
+        requesterID = req.getParameter(IRemoteRequest.CA_REVOKE_REQUESTER_ID);
 
         if (requesterID != null) {
             requesterID = requesterID.trim();
diff --git a/base/server/cms/src/com/netscape/cms/servlet/connector/GenerateKeyPairServlet.java b/base/server/cms/src/com/netscape/cms/servlet/connector/GenerateKeyPairServlet.java
index 3d0ad4c..ab01e29 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/connector/GenerateKeyPairServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/connector/GenerateKeyPairServlet.java
@@ -41,6 +41,8 @@ import com.netscape.certsrv.request.IRequestQueue;
 import com.netscape.cms.servlet.base.CMSServlet;
 import com.netscape.cms.servlet.common.CMSRequest;
 
+import org.dogtagpki.server.connector.IRemoteRequest;
+
 /**
  * GenerateKeyPairServlet
  * handles "server-side key pair generation" requests from the
@@ -134,13 +136,13 @@ public class GenerateKeyPairServlet extends CMSServlet {
 
         CMS.debug("processServerSideKeyGen begins:");
 
-        String rCUID = req.getParameter("CUID");
-        String rUserid = req.getParameter("userid");
-        String rdesKeyString = req.getParameter("drm_trans_desKey");
-        String rArchive = req.getParameter("archive");
-        String rKeysize = req.getParameter("keysize");
-        String rKeytype = req.getParameter("keytype");
-        String rKeycurve = req.getParameter("eckeycurve");
+        String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID);
+        String rUserid = req.getParameter(IRemoteRequest.KRA_UserId);
+        String rdesKeyString = req.getParameter(IRemoteRequest.KRA_Trans_DesKey);
+        String rArchive = req.getParameter(IRemoteRequest.KRA_KEYGEN_Archive);
+        String rKeysize = req.getParameter(IRemoteRequest.KRA_KEYGEN_KeySize);
+        String rKeytype = req.getParameter(IRemoteRequest.KRA_KEYGEN_KeyType);
+        String rKeycurve = req.getParameter(IRemoteRequest.KRA_KEYGEN_EC_KeyCurve);
 
         if ((rCUID == null) || (rCUID.equals(""))) {
             CMS.debug("GenerateKeyPairServlet: processServerSideKeygen(): missing request parameter: CUID");
@@ -236,15 +238,15 @@ public class GenerateKeyPairServlet extends CMSServlet {
           status = "4";
         */
         if (!status.equals("0"))
-            value = "status=" + status;
+            value = IRemoteRequest.RESPONSE_STATUS +"=" + status;
         else {
             StringBuffer sb = new StringBuffer();
-            sb.append("status=0&");
-            sb.append("wrapped_priv_key=");
+            sb.append(IRemoteRequest.RESPONSE_STATUS +"=0&");
+            sb.append(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey+ "=");
             sb.append(wrappedPrivKeyString);
-            sb.append("&iv_param=");
+            sb.append("&"+ IRemoteRequest.KRA_RESPONSE_IV_Param+ "=");
             sb.append(ivString);
-            sb.append("&public_key=");
+            sb.append("&"+ IRemoteRequest.KRA_RESPONSE_PublicKey+ "=");
             sb.append(publicKeyString);
             value = sb.toString();
 
diff --git a/base/server/cms/src/com/netscape/cms/servlet/connector/TokenKeyRecoveryServlet.java b/base/server/cms/src/com/netscape/cms/servlet/connector/TokenKeyRecoveryServlet.java
index cd3c22a..1f4f75c 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/connector/TokenKeyRecoveryServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/connector/TokenKeyRecoveryServlet.java
@@ -39,6 +39,8 @@ import com.netscape.certsrv.request.IRequestQueue;
 import com.netscape.cms.servlet.base.CMSServlet;
 import com.netscape.cms.servlet.common.CMSRequest;
 
+import org.dogtagpki.server.connector.IRemoteRequest;
+
 /**
  * TokenKeyRecoveryServlet
  * handles "key recovery service" requests from the
@@ -154,11 +156,11 @@ public class TokenKeyRecoveryServlet extends CMSServlet {
 
         CMS.debug("processTokenKeyRecovery begins:");
 
-        String rCUID = req.getParameter("CUID");
-        String rUserid = req.getParameter("userid");
-        String rKeyid = req.getParameter("keyid");
-        String rdesKeyString = req.getParameter("drm_trans_desKey");
-        String rCert = req.getParameter("cert");
+        String rCUID = req.getParameter(IRemoteRequest.TOKEN_CUID);
+        String rUserid = req.getParameter(IRemoteRequest.KRA_UserId);
+        String rKeyid = req.getParameter(IRemoteRequest.KRA_RECOVERY_KEYID);
+        String rdesKeyString = req.getParameter(IRemoteRequest.KRA_Trans_DesKey);
+        String rCert = req.getParameter(IRemoteRequest.KRA_RECOVERY_CERT);
 
         if ((rCUID == null) || (rCUID.equals(""))) {
             CMS.debug("TokenKeyRecoveryServlet: processTokenKeyRecovery(): missing request parameter: CUID");
@@ -256,12 +258,12 @@ public class TokenKeyRecoveryServlet extends CMSServlet {
             value = "status=" + status;
         else {
             StringBuffer sb = new StringBuffer();
-            sb.append("status=0&");
-            sb.append("wrapped_priv_key=");
+            sb.append(IRemoteRequest.RESPONSE_STATUS +"=0&");
+            sb.append(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey +"=");
             sb.append(wrappedPrivKeyString);
-            sb.append("&public_key=");
+            sb.append("&"+ IRemoteRequest.KRA_RESPONSE_PublicKey +"=");
             sb.append(publicKeyString);
-            sb.append("&iv_param=");
+            sb.append("&"+ IRemoteRequest.KRA_RESPONSE_IV_Param +"=");
             sb.append(ivString);
             value = sb.toString();
 
diff --git a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
index 7697b56..d6978e0 100644
--- a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
+++ b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
@@ -25,9 +25,16 @@ package org.dogtagpki.server.connector;
  * @author cfu
  */
 public interface IRemoteRequest {
+//    public static final String TOKEN_CUID = "CUID";
+    public static final String TOKEN_CUID = "tokencuid";
+    public static final String GET_XML = "xml";
+    public static final int RESPONSE_STATUS_NOT_FOUND = -1;
+    public static final String RESPONSE_STATUS = "status";
+    public static final String RESPONSE_ERROR_STRING = "error";
+    public static final String RESPONSE_STATUS_XML = "Status";
 
+    // TKS request params
     public static final String SERVER_SIDE_KEYGEN = "serversideKeygen";
-    public static final String TOKEN_CUID = "CUID";
     public static final String TOKEN_CARD_CHALLENGE = "card_challenge";
     public static final String TOKEN_HOST_CHALLENGE = "host_challenge";
     public static final String TOKEN_KEYINFO = "KeyInfo";
@@ -37,9 +44,7 @@ public interface IRemoteRequest {
     public static final String TOKEN_NEW_KEYINFO = "newKeyInfo";
     public static final String TOKEN_DATA = "data";
 
-    public static final int RESPONSE_STATUS_NOT_FOUND = -1;
-    public static final String RESPONSE_STATUS = "status";
-
+    // TKS response params
     /* computeSessionKey responses */
     public static final String TKS_RESPONSE_SessionKey = "sessionKey";
     public static final String TKS_RESPONSE_EncSessionKey = "encSessionKey";
@@ -57,4 +62,43 @@ public interface IRemoteRequest {
     /* computeRandomData response */
     public static final String TKS_RESPONSE_RandomData = "randomData";
 
+    // CA request params
+    public static final String CA_ProfileId = "profileId";
+    public static final String CA_ENROLL_screenname = "screenname";
+    public static final String CA_ENROLL_publickey = "publickey";
+    public static final String CA_RenewedCertificate = "renewedCertificate";
+    public static final String CA_RENEWAL_SerialNum = "serial_num";
+    public static final String CA_RENEWAL= "renewal";
+
+    public static final String CA_REVOKE = "revoke";
+    public static final String CA_REVOCATION_REASON = "revocationReason";
+    public static final String CA_REVOKE_ALL = "revokeAll";
+    public static final String CA_REVOKE_SERIAL = "certRecordId";
+    public static final String CA_REVOKE_COUNT = "totalRecordCount";
+    public static final String CA_REVOKE_INVALID_DATE = "invalidityDate";
+    public static final String CA_REVOKE_REQUESTER_COMMENTS = "revRequesterComments";
+    public static final String CA_REVOKE_REQUESTER_ID = "revRequesterID";
+    public static final String CA_UNREVOKE_SERIAL = "serialNumber";
+
+    // CA response params
+    public static final String CA_OP = "op";
+    public static final String CA_RESPONSE_RenewedCertificate_x509 = "X509Certificate";
+    public static final String CA_RESPONSE_RenewedCertificate_b64 = "b64";
+    public static final String CA_RESPONSE_RenewedCertificate_SubjectDN = "SubjectDN";
+    public static final String CA_RESPONSE_RenewedCertificate_serial = "serialno";
+
+    // KRA request params
+    public static final String KRA_UserId = "userid";
+    public static final String KRA_Trans_DesKey = "drm_trans_desKey";
+    public static final String KRA_KEYGEN_Archive = "archive";
+    public static final String KRA_KEYGEN_KeyType = "keytype";
+    public static final String KRA_KEYGEN_EC_KeyCurve = "eckeycurve";
+    public static final String KRA_KEYGEN_KeySize = "keysize";
+    public static final String KRA_RECOVERY_CERT = "cert";
+    public static final String KRA_RECOVERY_KEYID = "keyid";
+
+    // KRA response params
+    public static final String KRA_RESPONSE_PublicKey = "public_key";
+    public static final String KRA_RESPONSE_Wrapped_PrivKey = "wrapped_priv_key";
+    public static final String KRA_RESPONSE_IV_Param = "iv_param";
 }
diff --git a/base/tps-tomcat/shared/conf/CS.cfg.in b/base/tps-tomcat/shared/conf/CS.cfg.in
index f8ce2ae..3ef7863 100644
--- a/base/tps-tomcat/shared/conf/CS.cfg.in
+++ b/base/tps-tomcat/shared/conf/CS.cfg.in
@@ -1549,7 +1549,7 @@ tps.connector.kra1.minHttpConns=1
 tps.connector.kra1.maxHttpConns=15
 tps.connector.kra1.nickName=[HSM_LABEL]:[NICKNAME]
 tps.connector.kra1.timeout=30
-tps.connector.kra1.uri.GenerateKeyPair=/kra/agent/kra/connector
+tps.connector.kra1.uri.GenerateKeyPair=/kra/agent/kra/GenerateKeyPair
 tps.connector.kra1.uri.TokenKeyRecovery=/kra/agent/kra/TokenKeyRecovery
 tps.connector.tks1._001=#########################################
 tps.connector.tks1._002=# TKS connector
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java
new file mode 100644
index 0000000..7e2a298
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CAEnrollCertResponse.java
@@ -0,0 +1,52 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.security.cert.X509Certificate;
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * CAEnrollCertResponse is the class for the response to
+ * CA Remote Request: enrollCertificate()
+ *
+ */
+public class CAEnrollCertResponse extends RemoteResponse
+{
+    public CAEnrollCertResponse(Hashtable<String, Object> ht) {
+        nameValTable = ht;
+    }
+
+    public String getRenewedCertB64() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64);
+    }
+
+    public String getRenewedCertSerialHex() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial);
+    }
+
+    public String getRenewedCertSubjectDN() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN);
+    }
+
+    public X509Certificate getRenewedCert() {
+        return (X509Certificate) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_x509);
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java
new file mode 100644
index 0000000..81823fa
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java
@@ -0,0 +1,672 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import netscape.security.x509.AuthorityKeyIdentifierExtension;
+import netscape.security.x509.KeyIdentifier;
+import netscape.security.x509.PKIXExtensions;
+import netscape.security.x509.RevocationReason;
+import netscape.security.x509.SubjectKeyIdentifierExtension;
+import netscape.security.x509.X509CertImpl;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+import org.dogtagpki.server.tps.TPSSubsystem;
+import org.dogtagpki.tps.main.TPSBuffer;
+import org.dogtagpki.tps.main.Util;
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.CryptoManager.NotInitializedException;
+import org.mozilla.jss.crypto.X509Certificate;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.cmscore.connector.HttpConnector;
+import com.netscape.cmsutil.http.HttpResponse;
+import com.netscape.cmsutil.xml.XMLObject;
+
+/**
+ * CARemoteRequestHandler is a class representing remote requests
+ * offered by the Certificate Authority (CA)
+ *
+ * @author cfu
+ */
+public class CARemoteRequestHandler extends RemoteRequestHandler
+{
+    public CARemoteRequestHandler(String connID)
+            throws EBaseException {
+        if (connID == null) {
+            throw new EBaseException("CARemoteRequestHandler: CARemoteRequestHandler(): connID null.");
+        }
+        connid = connID;
+    }
+
+    /**
+     * enrollCertificate enrolls a certificate in the CA
+     *
+     * TODO: certificate and key structures conversion
+     * to be covered by ticket #941 once we have
+     * dependency such as secure channel in place
+     * for better testing
+     *
+     * @param pubKeybuf public key for enrollment
+     * @param uid uid for enrollment
+     * @param cuid token id
+     *
+     * @returns CAEnrollCertResponse
+     */
+    public CAEnrollCertResponse enrollCertificate(
+            TPSBuffer pubKeybuf,
+            String uid,
+            String cuid)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: enrollCertificate(): begins.");
+        if ((pubKeybuf == null) || (uid == null) || (cuid == null)) {
+            throw new EBaseException("CARemoteRequestHandler: enrollCertificate(): input parameter null.");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
+        /*
+         * TODO: obtain the profile name from current session when available.
+         *   Hard-code to "userKey" for now
+         */
+        String profileId =
+                conf.getString("op.enroll.userKey.keyGen.signing.ca.profileId",
+                        "caTokenUserSigningKeyEnrollment");
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("CARemoteRequestHandler: enrollCertificate(): sending request to CA");
+        HttpResponse resp =
+                conn.send("enrollment",
+                        IRemoteRequest.GET_XML + "=" +
+                                true +
+                                "&" + IRemoteRequest.TOKEN_CUID + "=" +
+                                cuid +
+                                "&" + IRemoteRequest.CA_ENROLL_screenname + "=" +
+                                uid +
+                                "&" + IRemoteRequest.CA_ENROLL_publickey + "=" +
+                                Util.specialEncode(pubKeybuf) +
+                                "&" + IRemoteRequest.CA_ProfileId + "=" +
+                                profileId);
+
+        String content = resp.getContent();
+
+        CMS.debug("CARemoteRequestHandler: enrollCertificate(): got content = " + content);
+
+        XMLObject xmlResponse =
+                getXMLparser(content);
+
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    new Hashtable<String, Object>();
+
+            CMS.debug("CARemoteRequestHandler: enrollCertificate(): received:" +
+                    content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             * Note: serverKeygen and !serverKeygen returns different set of
+             * response values so "missing" might not be bad
+             */
+            String value = null;
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            value = xmlResponse.getValue(IRemoteRequest.RESPONSE_STATUS_XML);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler: enrollCertificate(): Status not found.");
+                CMS.debug("CARemoteRequestHandler: enrollCertificate(): got content = " + content);
+            } else {
+                CMS.debug("CARemoteRequestHandler: enrollCertificate(): got Status = " + value);
+                ist = Integer.parseInt(value);
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = xmlResponse.getValue("SubjectDN");
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: enrollCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: enrollCertificate(): got IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN = "
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN, value);
+            }
+
+            value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: enrollCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: enrollCertificate(): got IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial = 0x"
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial, value);
+            }
+
+            value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: enrollCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: enrollCertificate(): got IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64 = "
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64, value);
+                try {
+                    X509Certificate newCert = (X509Certificate) new X509CertImpl(CMS.AtoB(value));
+                    response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_x509, newCert);
+                    CMS.debug("CARemoteRequestHandler: enrollCertificate(): new cert parsed successfully");
+                } catch (CertificateException e) {
+                    // we don't exit.  Keep going.
+                    CMS.debug("CARemoteRequestHandler: enrollCertificate(): exception:" + e);
+                }
+            }
+
+            CMS.debug("CARemoteRequestHandler: enrollCertificate(): ends.");
+            return new CAEnrollCertResponse(response);
+        } else {
+            CMS.debug("CARemoteRequestHandler: enrollCertificate(): no response content");
+            throw new EBaseException("CARemoteRequestHandler: enrollCertificate(): no response content.");
+        }
+    }
+
+    /**
+     * renewCertificate renew a certificate by serial number
+     *
+     * @param serialno the serial number of the cert to be renewed
+     * @return CARenewCertResponse
+     */
+    public CARenewCertResponse renewCertificate(BigInteger serialno)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: renewCertificate(): begins.");
+        if (serialno == null) {
+            throw new EBaseException("CARemoteRequestHandler: renewCertificate(): input parameter null.");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
+
+        /*
+         * TODO: obtain the profile name from current session when available.
+         *   Hard-code to "userKey" for now
+         */
+        String profileId =
+                conf.getString("op.enroll.userKey.renewal.signing.ca.profileId",
+                        "caTokenUserEncryptionKeyRenewal");
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("CARemoteRequestHandler: renewCertificate(): sending request to CA");
+        HttpResponse resp =
+                conn.send("renewal",
+                        IRemoteRequest.GET_XML + "=" + true +
+                                "&" + IRemoteRequest.CA_RENEWAL + "=" + true +
+                                "&" + IRemoteRequest.CA_RENEWAL_SerialNum + "=" + serialno.toString() +
+                                "&" + IRemoteRequest.CA_ProfileId + "=" + profileId);
+
+        String content = resp.getContent();
+
+        XMLObject xmlResponse =
+                getXMLparser(content);
+
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    new Hashtable<String, Object>();
+
+            CMS.debug("CARemoteRequestHandler: renewCertificate(): received:" +
+                    content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             * Note: serverKeygen and !serverKeygen returns different set of
+             * response values so "missing" might not be bad
+             */
+            String value = null;
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            value = xmlResponse.getValue(IRemoteRequest.RESPONSE_STATUS_XML);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler: renewCertificate(): Status not found.");
+                CMS.debug("CARemoteRequestHandler: renewCertificate(): got content = " + content);
+            } else {
+                CMS.debug("CARemoteRequestHandler: renewCertificate(): got Status = " + value);
+                ist = Integer.parseInt(value);
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = xmlResponse.getValue("SubjectDN");
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: renewCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: renewCertificate(): got IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN = "
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN, value);
+            }
+
+            value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: renewCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: renewCertificate(): got IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial = 0x"
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial, value);
+            }
+
+            value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: renewCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: renewCertificate(): got IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64 = "
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64, value);
+                try {
+                    X509Certificate newCert = (X509Certificate) new X509CertImpl(CMS.AtoB(value));
+                    response.put(IRemoteRequest.CA_RESPONSE_RenewedCertificate_x509, newCert);
+                    CMS.debug("CARemoteRequestHandler: renewCertificate(): new cert parsed successfully");
+                } catch (CertificateException e) {
+                    // we don't exit.  Keep going.
+                    CMS.debug("CARemoteRequestHandler: renewCertificate(): exception:" + e);
+                }
+            }
+
+            CMS.debug("CARemoteRequestHandler: renewCertificate(): ends.");
+            return new CARenewCertResponse(response);
+        } else {
+            CMS.debug("CARemoteRequestHandler: renewCertificate(): no response content");
+            throw new EBaseException("CARemoteRequestHandler: renewCertificate(): no response content.");
+        }
+    }
+
+    /**
+     * revokeCertificate provides the basic revocation of a certificate from
+     * the CA
+     *
+     * @param serialno serial number of the cert to revoke
+     * @param reason reason to revoke per definition in RevocationReason
+     *
+     * @returns CARevokeCertResponse
+     */
+    private CARevokeCertResponse revokeCertificate(
+            BigInteger serialno,
+            RevocationReason reason)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: revokeCertificate(): begins.");
+        if ((serialno == null) || (reason == null)) {
+            throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter null.");
+        }
+
+        // IConfigStore conf = CMS.getConfigStore();
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("CARemoteRequestHandler: revokeCertificate(): sending request to CA");
+        HttpResponse resp =
+                conn.send("revoke",
+                        IRemoteRequest.CA_OP + "=" + IRemoteRequest.CA_REVOKE +
+                                "&" + IRemoteRequest.CA_REVOCATION_REASON + "=" + reason.getCode() +
+                                "&" + IRemoteRequest.CA_REVOKE_ALL + "=(" +
+                                IRemoteRequest.CA_REVOKE_SERIAL + "=" + serialno.toString() + ")&" +
+                                IRemoteRequest.CA_REVOKE_COUNT + "=1");
+        String content = resp.getContent();
+
+        CMS.debug("CARemoteRequestHandler: revokeCertificate(): got content = " + content);
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             */
+            String value = null;
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            value = (String) response.get(IRemoteRequest.RESPONSE_STATUS);
+
+            CMS.debug("CARemoteRequestHandler: revokeCertificate(): got status = " + value);
+            ist = Integer.parseInt(value);
+            if (ist != 0) {
+                CMS.debug("CARemoteRequestHandler: revokeCertificate(): status not 0, getting error string... ");
+                value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+                if (value == null) {
+                    CMS.debug("CARemoteRequestHandler: revokeCertificate(): response missing name-value pair for: " +
+                            IRemoteRequest.RESPONSE_ERROR_STRING);
+                } else {
+                    CMS.debug("CARemoteRequestHandler: revokeCertificate(): got IRemoteRequest.RESPONSE_ERROR_STRING = "
+                            + value);
+                    response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value);
+                }
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            CMS.debug("CARemoteRequestHandler: revokeCertificate(): ends.");
+            return new CARevokeCertResponse(response);
+        } else {
+            CMS.debug("CARemoteRequestHandler: revokeCertificate(): no response content.");
+            throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): no response content.");
+        }
+    }
+
+    /**
+     * unrevokeCertificate provides the basic unrevocation of a certificate from
+     * the CA
+     *
+     * @param serialno serial number of the cert to unrevoke
+     *
+     * @returns CARevokeCertResponse
+     */
+    private CARevokeCertResponse unrevokeCertificate(
+            BigInteger serialno)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): begins.");
+        if (serialno == null) {
+            throw new EBaseException("CARemoteRequestHandler: unrevokeCertificate(): input parameter null.");
+        }
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): sending request to CA");
+        HttpResponse resp =
+                conn.send("unrevoke",
+                        IRemoteRequest.CA_UNREVOKE_SERIAL + "=" + serialno.toString());
+        String content = resp.getContent();
+
+        CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got content = " + content);
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             */
+            String value = null;
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            value = (String) response.get(IRemoteRequest.RESPONSE_STATUS);
+
+            CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got status = " + value);
+            ist = Integer.parseInt(value);
+            if (ist != 0) {
+                CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): status not 0, getting error string... ");
+                value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+                if (value == null) {
+                    CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): response missing name-value pair for: " +
+                            IRemoteRequest.RESPONSE_ERROR_STRING);
+                } else {
+                    CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got IRemoteRequest.RESPONSE_ERROR_STRING = "
+                            + value);
+                    response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value);
+                }
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): ends.");
+            return new CARevokeCertResponse(response);
+        } else {
+            CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): no response content.");
+            throw new EBaseException("CARemoteRequestHandler: unrevokeCertificate(): no response content.");
+        }
+    }
+
+    /**
+     * revokeFromOtherCA searches through all defined ca entries
+     * to find the cert's signing ca for revocation / unrevocation.
+     * It is called from revokeCertificate() when the cert's
+     * AKI does not match that of the current signing ca.
+     *
+     * @param revoke true to revoke; false to unrevoke
+     * @param cert cert to (un)revoke
+     * @param serialno parameter for the (Un)RevokeCertificate() functions
+     * @param reason RevocationReason for the base revokeCertificate() function
+     */
+    private CARevokeCertResponse revokeFromOtherCA(
+            boolean revoke, // true==revoke; false==unrevoke
+            X509CertImpl cert,
+            RevocationReason reason)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: revokeFromOtherCA: begins");
+        if (cert == null) {
+            throw new EBaseException("CARemoteRequestHandler: revokeFromOtherCA(): input parameter cert null.");
+        }
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        ArrayList<String> caList =
+                subsystem.getConnectionManager().getCAList();
+
+        String certAkiString = null;
+        try {
+            certAkiString = getCertAkiString(cert);
+        } catch (IOException e) {
+            throw new EBaseException("thrown from revokeFromOtherCA");
+        }
+        Iterator<String> iterator = caList.iterator();
+        while (iterator.hasNext()) {
+            try {
+                String caSkiString = getCaSki(iterator.next());
+                if (certAkiString.equals(caSkiString)) {
+                    CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() cert AKI and caCert SKI matched");
+                    if (revoke) {
+                        return revokeCertificate(cert.getSerialNumber(), reason);
+                    } else {
+                        return unrevokeCertificate(cert.getSerialNumber());
+                    }
+                } else { // not a match then iterate to next ca in list
+                    CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() cert AKI and caCert SKI not matched");
+                }
+            } catch (Exception e) {
+                // any issue then iterate to next ca in list
+                CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() issue found, iterate to next ca in list. Exception:"
+                        + e);
+            }
+        }
+        throw new EBaseException("revokeFromOtherCA: signing ca not found");
+    }
+
+    /**
+     * getCaSki returns the CA's Subject Key Identifier (ski)
+     * associated with the connector id.
+     * If the ca's ski has not been calculated, it will do so and
+     * save to the connector's caSKI config entry
+     *
+     * @param conn connector id
+     * @returns ca's ski associated with conn
+     */
+    private String getCaSki(String conn)
+            throws EBaseException, IOException {
+
+        String caSkiString = null;
+        if (conn == null) {
+            throw new EBaseException("CARemoteRequestHandler: getCaSki(): input parameter conn null.");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
+
+        /*
+         * first, see if ca Subject Key Identifier (SKI) is in
+         * config store. If not, put it in, so we don't have to
+         * calculate that every time.
+         */
+        boolean foundCaSKI = false;
+        String caSKI = null;
+        try {
+            String configName = "tps.connector." + conn + ".caSKI";
+            CMS.debug("CARemoteRequestHandler: getCaSki() retriving configName=" + configName);
+            caSKI =
+                    conf.getString(configName);
+            CMS.debug("CARemoteRequestHandler: getCaSki() caSKI found for connector:" + conn);
+            foundCaSKI = true;
+            caSkiString = caSKI;
+        } catch (EBaseException e) {
+            // caSKI not yet calculated; proceed to calculate
+            CMS.debug("CARemoteRequestHandler: getCaSki() caSKI not yet calculated:" + e);
+        }
+
+        if (!foundCaSKI) { // get from cert db
+            try {
+                String caNickname =
+                        conf.getString("tps.connector." + conn + ".caNickname");
+                CMS.debug("CARemoteRequestHandler: getCaSki() Calculating caSKI...searching for ca cert in nss db:"
+                        + caNickname);
+                CryptoManager cm = CryptoManager.getInstance();
+                try {
+                    X509Certificate c = cm.findCertByNickname(caNickname);
+                    X509CertImpl caCert = new X509CertImpl(c.getEncoded());
+                    // now retrieve caSKI and store in config
+                    caSkiString = getCertSkiString(caCert);
+                    CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculated. Saving it.");
+                    conf.putString("tps.connector." + conn + ".caSKI", caSkiString);
+                    conf.commit(false);
+                } catch (IOException e) {
+                    throw new IOException("thrown from getCaSki");
+                } catch (Exception et) {
+                    /* ca cert not found in nss db; no match needed */
+                    CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculation failure." + et);
+                    throw new EBaseException("CARemoteRequestHandler: getCaSki(): skip match.");
+                }
+            } catch (EBaseException e) {
+                /*
+                 *  if it gets here, that means config is missing both:
+                 *  1. tps.connector.ca<n>.caSKI
+                 *  2. tps.connector.ca<n>.caNickname
+                 *  now assume default of just using the issuing ca and
+                 *  no search performed
+                 */
+                CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculation failure." + e);
+                throw new EBaseException("CARemoteRequestHandler: getCaSki(): skip match.:" + e);
+            } catch (NotInitializedException e) {
+                CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculation failure." + e);
+                throw new EBaseException("CARemoteRequestHandler: getCaSki(): skip match.:" + e);
+            }
+        }
+
+        return caSkiString;
+    }
+
+    private String getCertAkiString(X509CertImpl cert)
+            throws EBaseException, IOException {
+        if (cert == null) {
+            throw new EBaseException("CARemoteRequestHandler: getCertAkiString(): input parameter cert null.");
+        }
+        AuthorityKeyIdentifierExtension certAKI =
+                (AuthorityKeyIdentifierExtension)
+                cert.getExtension(PKIXExtensions.AuthorityKey_Id.toString());
+        KeyIdentifier kid =
+                (KeyIdentifier) certAKI.get(AuthorityKeyIdentifierExtension.KEY_ID);
+        return (CMS.BtoA(kid.getIdentifier()).trim());
+    }
+
+    private String getCertSkiString(X509CertImpl cert)
+            throws EBaseException, IOException {
+        if (cert == null) {
+            throw new EBaseException("CARemoteRequestHandler: getCertSkiString(): input parameter cert null.");
+        }
+        SubjectKeyIdentifierExtension certSKI =
+                (SubjectKeyIdentifierExtension)
+                cert.getExtension(PKIXExtensions.SubjectKey_Id.toString());
+        KeyIdentifier kid =
+                (KeyIdentifier) certSKI.get(SubjectKeyIdentifierExtension.KEY_ID);
+        return (CMS.BtoA(kid.getIdentifier()).trim());
+    }
+
+    /**
+     * revokeCertificate() supports revocation routing by providing
+     * CA discovery. When needed, it searchs through all listed ca
+     * entries to find the cert's signing ca for revocation.
+     *
+     * Note: in the configuration, the ca signing cert of each ca
+     * id must be imported into the db and have its nickname present.
+     * e.g. tps.connector.ca1.caNickname=CA1nickname
+     *
+     * See design:
+     * http://pki.fedoraproject.org/wiki/TPS_-_Revocation_Routing
+     *
+     * @param revoke true to revoke; false to unrevoke
+     * @param serialno serial number for the (Un)RevokeCertificate() functions
+     * @param reason RevocationReason for the base revokeCertificate() function
+     */
+    public CARevokeCertResponse revokeCertificate(
+            boolean revoke, // true==revoke; false==unrevoke
+            X509CertImpl cert,
+            RevocationReason reason)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: revokeCertificate() begins with CA discovery");
+        if (cert == null) {
+            throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter cert null.");
+        }
+        if ((revoke == true) && (reason == null)) {
+            throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter reason null.");
+        }
+
+        boolean skipMatch = false;
+
+        String caSkiString = null;
+        String certAkiString = null;
+
+        try {
+            caSkiString = getCaSki(connid);
+            certAkiString = getCertAkiString(cert);
+        } catch (IOException e) {
+            skipMatch = true;
+        }
+        if (!skipMatch) {
+            /* now compare cert's AKI to the ca's SKI
+             *   if matched, continue,
+             *   if not, search in the ca list
+             */
+            CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI matching begins");
+            if (certAkiString.equals(caSkiString)) {
+                CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI matched");
+                if (revoke) {
+                    return revokeCertificate(cert.getSerialNumber(), reason);
+                } else {
+                    return unrevokeCertificate(cert.getSerialNumber());
+                }
+            } else {
+                CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI of the designated issuing ca do not match...calling revokeFromOtherCA to search for another ca");
+                return revokeFromOtherCA(revoke, cert, reason);
+            }
+        } else {
+            if (revoke) {
+                return revokeCertificate(cert.getSerialNumber(), reason);
+            } else {
+                return unrevokeCertificate(cert.getSerialNumber());
+            }
+        }
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java
new file mode 100644
index 0000000..cf7f877
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARenewCertResponse.java
@@ -0,0 +1,52 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.security.cert.X509Certificate;
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * CARenewCertResponse is the class for the response to
+ * CA Remote Request: renewCertificate()
+ *
+ */
+public class CARenewCertResponse extends RemoteResponse
+{
+    public CARenewCertResponse(Hashtable<String, Object> ht) {
+        nameValTable = ht;
+    }
+
+    public String getRenewedCertB64() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_b64);
+    }
+
+    public String getRenewedCertSerialHex() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_serial);
+    }
+
+    public String getRenewedCertSubjectDN() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_SubjectDN);
+    }
+
+    public X509Certificate getRenewedCert() {
+        return (X509Certificate) nameValTable.get(IRemoteRequest.CA_RESPONSE_RenewedCertificate_x509);
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java
new file mode 100644
index 0000000..d7db597
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARevokeCertResponse.java
@@ -0,0 +1,39 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * CARevokeCertResponse is the class for the response to
+ * CA Remote Request: revoteCertificate()
+ *
+ */
+public class CARevokeCertResponse extends RemoteResponse
+{
+    public CARevokeCertResponse(Hashtable<String, Object> ht) {
+        nameValTable = ht;
+    }
+
+    public String getErrorString() {
+        return (String) nameValTable.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java
index 607f3c9..60cb6e2 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java
@@ -18,6 +18,7 @@
 
 package org.dogtagpki.server.tps.cms;
 
+import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
 
@@ -35,13 +36,43 @@ import com.netscape.cmscore.connector.RemoteAuthority;
 /**
  * ConnectionManager is a class for connection management
  * of its Remote Authorities
+ *
  * @author cfu
  */
 public class ConnectionManager
 {
     private Hashtable<String, IConnector> connectors;
+    ArrayList<String> caList;
 
     public ConnectionManager() {
+        // initialize the ca list for revocation routing:
+        //    tps.connCAList=ca1,ca2...ca<n>
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        IConfigStore conf = subsystem.getConfigStore();
+        String caListString;
+        try {
+            caListString = conf.getString("connCAList");
+            CMS.debug("ConnectionManager: ConnectionManager(): Initializing CA routing list");
+        } catch (EBaseException e) {
+            CMS.debug("ConnectionManager: ConnectionManager(): no connCAList for ca discovery.  No revocation routing");
+            return;
+        }
+
+        caList = new ArrayList<String>();
+
+        String[] elements = caListString.split(",");
+        CMS.debug("ConnectionManager: ConnectionManager(): # of ca list elements:" +
+                elements.length);
+        for (String ca : elements) {
+            caList.add(ca);
+            CMS.debug("ConnectionManager: ConnectionManager(): ca connector id added to revocation routing list:" + ca);
+        }
+        CMS.debug("ConnectionManager: ConnectionManager(): CA routing list initialized.");
+    }
+
+    public ArrayList<String> getCAList() {
+        return caList;
     }
 
     /*
@@ -70,16 +101,16 @@ public class ConnectionManager
     public void initConnectors() throws EBaseException {
         CMS.debug("ConnectionManager: initConnectors(): begins.");
         TPSSubsystem subsystem =
-            (TPSSubsystem)CMS.getSubsystem(TPSSubsystem.ID);
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
         IConfigStore conf = subsystem.getConfigStore();
         IConfigStore connectorSubstore = conf.getSubStore("connector");
         Enumeration<String> connector_enu = connectorSubstore.getSubStoreNames();
         connectors = new Hashtable<String, IConnector>();
         while (connector_enu.hasMoreElements()) {
             String connectorID = connector_enu.nextElement();
-            CMS.debug("ConnectionManager: initConnectors(): initializing connector "+connectorID);
+            CMS.debug("ConnectionManager: initConnectors(): initializing connector " + connectorID);
             IConfigStore connectorConfig =
-                connectorSubstore.getSubStore(connectorID);
+                    connectorSubstore.getSubStore(connectorID);
             IConnector conn = null;
             boolean enable = connectorConfig.getBoolean("enable", false);
             if (!enable) {
@@ -91,7 +122,7 @@ public class ConnectionManager
 
             connectors.put(connectorID, conn);
             CMS.debug("ConnectionManager: initConnectors(): connector "
-                    +connectorID+
+                    + connectorID +
                     " initialized.");
         }
         CMS.debug("ConnectionManager: initConnectors(): ends.");
@@ -108,7 +139,7 @@ public class ConnectionManager
         IConnector connector = null;
 
         CMS.debug("ConnectionManager: createConnector(): begins.");
-        if (conf== null || conf.size() <= 0) {
+        if (conf == null || conf.size() <= 0) {
             CMS.debug("ConnectionManager: createConnector(): conf null or empty.");
             throw new EBaseException("called with null config store");
         }
@@ -127,19 +158,20 @@ public class ConnectionManager
             CMS.debug("ConnectionManager: createConnector(): uri(s) not found in config.");
             throw new EBaseException("uri(s) not found in config");
         }
-        CMS.debug("ConnectionManager: createConnector(): uriSubstore name="+uriSubstore.getName()+" size =" +uriSubstore.size());
+        CMS.debug("ConnectionManager: createConnector(): uriSubstore name=" + uriSubstore.getName() + " size ="
+                + uriSubstore.size());
 
         Enumeration<String> uri_enu = uriSubstore.getPropertyNames();
         while (uri_enu.hasMoreElements()) {
             String op = uri_enu.nextElement();
             if ((op != null) && !op.equals(""))
-                CMS.debug("ConnectionManager: createConnector(): op name="+op);
+                CMS.debug("ConnectionManager: createConnector(): op name=" + op);
             else
                 continue;
 
             String uriValue = uriSubstore.getString(op);
             if ((uriValue != null) && !uriValue.equals(""))
-                CMS.debug("ConnectionManager: createConnector(): uri value="+uriValue);
+                CMS.debug("ConnectionManager: createConnector(): uri value=" + uriValue);
             else
                 continue;
             uris.put(op, uriValue);
@@ -147,7 +179,7 @@ public class ConnectionManager
 
         String nickname = conf.getString("nickName", null);
         if (nickname != null)
-            CMS.debug("ConnectionManager: createConnector(): nickName="+nickname);
+            CMS.debug("ConnectionManager: createConnector(): nickName=" + nickname);
         else {
             CMS.debug("ConnectionManager: createConnector(): nickName not found in config");
             throw new EBaseException("nickName not found in config");
@@ -157,15 +189,15 @@ public class ConnectionManager
         int resendInterval = -1;
         int timeout = conf.getInteger("timeout", 0);
         RemoteAuthority remauthority =
-            new RemoteAuthority(host, port, uris, timeout, MediaType.APPLICATION_FORM_URLENCODED);
+                new RemoteAuthority(host, port, uris, timeout, MediaType.APPLICATION_FORM_URLENCODED);
 
         CMS.debug("ConnectionManager: createConnector(): establishing HttpConnector");
         if (timeout == 0) {
             connector =
-                new HttpConnector(null, nickname, remauthority, resendInterval, conf);
+                    new HttpConnector(null, nickname, remauthority, resendInterval, conf);
         } else {
             connector =
-                new HttpConnector(null, nickname, remauthority, resendInterval, conf, timeout);
+                    new HttpConnector(null, nickname, remauthority, resendInterval, conf, timeout);
         }
 
         CMS.debug("ConnectionManager: createConnector(): ends.");
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java
new file mode 100644
index 0000000..9d0c5ff
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARecoverKeyResponse.java
@@ -0,0 +1,51 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * KRARecoverKeyResponse is the class for the response to
+ * KRA Remote Request: recoverKey()
+ *
+ */
+public class KRARecoverKeyResponse extends RemoteResponse
+{
+    public KRARecoverKeyResponse(Hashtable<String, Object> ht) {
+        nameValTable = ht;
+    }
+
+    public String getErrorString() {
+        return (String) nameValTable.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+    }
+
+    public String getPublicKey() {
+        return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_PublicKey);
+    }
+
+    public String getWrappedPrivKey() {
+        return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey);
+    }
+
+    public String getIVParam() {
+        return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_IV_Param);
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java
new file mode 100644
index 0000000..032c565
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRARemoteRequestHandler.java
@@ -0,0 +1,303 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+import org.dogtagpki.server.tps.TPSSubsystem;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.cmscore.connector.HttpConnector;
+import com.netscape.cmsutil.http.HttpResponse;
+
+/**
+ * KRARemoteRequestHandler is a class representing remote requests
+ * offered by the Key Recovery Authority (KRA)
+ * On a successful return, name/value pairs are provided in a Hashtable where
+ * all contents are URL decoded if needed
+ *
+ * @author cfu
+ */
+public class KRARemoteRequestHandler extends RemoteRequestHandler
+{
+    public KRARemoteRequestHandler(String connID)
+            throws EBaseException {
+        if (connID == null) {
+            throw new EBaseException("KRARemoteRequestHandler: KRARemoteRequestHandler(): connID null.");
+        }
+
+        connid = connID;
+    }
+
+    /**
+     * serverSideKeyGen generates key pairs on the KRA
+     *
+     * @param cuid is the token id
+     * @param userid is the user id
+     * @param sDesKey is the des key provided by the TKS for key encryption
+     * @param archive true or false
+     *
+     * @returns KRAServerSideKeyGenResponse
+     */
+    public KRAServerSideKeyGenResponse serverSideKeyGen(
+            boolean isECC,
+            int keysize,
+            String cuid,
+            String userid,
+            String sDesKey,
+            boolean archive)
+            throws EBaseException {
+
+        CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): begins.");
+        if ((cuid == null) || (userid == null) || (sDesKey == null)) {
+            throw new EBaseException("KRARemoteRequestHandler: serverSideKeyGen(): input parameter null.");
+        }
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): sending request to CA");
+        HttpResponse resp;
+        if (isECC) {
+            String eckeycurve;
+            if (keysize == 521) {
+                eckeycurve = "nistp521";
+            } else if (keysize == 384) {
+                eckeycurve = "nistp384";
+            } else if (keysize == 256) {
+                eckeycurve = "nistp256";
+            } else {
+                CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): unrecognized ECC keysize" + keysize
+                        + ", setting to nistp256");
+                keysize = 256;
+                eckeycurve = "nistp256";
+            }
+
+            resp =
+                    conn.send("GenerateKeyPair",
+                            IRemoteRequest.KRA_KEYGEN_Archive + "=" +
+                                    archive +
+                                    "&" + IRemoteRequest.TOKEN_CUID + "=" +
+                                    cuid +
+                                    "&" + IRemoteRequest.KRA_UserId + "=" +
+                                    userid +
+                                    "&" + IRemoteRequest.KRA_KEYGEN_KeyType + "=" +
+                                    "EC" +
+                                    "&" + IRemoteRequest.KRA_KEYGEN_EC_KeyCurve + "=" +
+                                    eckeycurve +
+                                    "&" + IRemoteRequest.KRA_Trans_DesKey + "=" +
+                                    sDesKey);
+        } else { // RSA
+            resp =
+                    conn.send("GenerateKeyPair",
+                            IRemoteRequest.KRA_KEYGEN_Archive + "=" +
+                                    archive +
+                                    "&" + IRemoteRequest.TOKEN_CUID + "=" +
+                                    cuid +
+                                    "&" + IRemoteRequest.KRA_UserId + "=" +
+                                    userid +
+                                    "&" + IRemoteRequest.KRA_KEYGEN_KeyType + "=" +
+                                    "RSA" +
+                                    "&" + IRemoteRequest.KRA_KEYGEN_KeySize + "=" +
+                                    keysize +
+                                    "&" + IRemoteRequest.KRA_Trans_DesKey + "=" +
+                                    sDesKey);
+        }
+
+        String content = resp.getContent();
+
+        CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): got content = " + content);
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             * Note: response values "missing" might not be bad for some cases
+             */
+            String value = null;
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            value = (String) response.get(IRemoteRequest.RESPONSE_STATUS);
+
+            CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): got status = " + value);
+            ist = Integer.parseInt(value);
+            if (ist != 0) {
+                CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): status not 0, getting error string... ");
+                value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+                if (value == null) {
+                    CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " +
+                            IRemoteRequest.RESPONSE_ERROR_STRING);
+                } else {
+                    CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): got IRemoteRequest.RESPONSE_ERROR_STRING = "
+                            + value);
+                    response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value);
+                }
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = (String) response.get(IRemoteRequest.KRA_RESPONSE_PublicKey);
+            if (value == null) {
+                CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " +
+                        IRemoteRequest.KRA_RESPONSE_PublicKey);
+            } else {
+                CMS.debug("KRARemoteRequestHandler:serverSideKeyGen(): got IRemoteRequest.KRA_RESPONSE_PublicKey= "
+                        + value);
+                response.put(IRemoteRequest.KRA_RESPONSE_PublicKey, value);
+            }
+
+            value = (String) response.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey);
+            if (value == null) {
+                CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " +
+                        IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey);
+            } else {
+                CMS.debug("KRARemoteRequestHandler:serverSideKeyGen(): got IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey= "
+                        + value);
+                response.put(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey, value);
+            }
+
+            value = (String) response.get(IRemoteRequest.KRA_RESPONSE_IV_Param);
+            if (value == null) {
+                CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): response missing name-value pair for: " +
+                        IRemoteRequest.KRA_RESPONSE_IV_Param);
+            } else {
+                CMS.debug("KRARemoteRequestHandler:serverSideKeyGen(): got IRemoteRequest.KRA_RESPONSE_IV_Param= "
+                        + value);
+                response.put(IRemoteRequest.KRA_RESPONSE_IV_Param, value);
+            }
+
+            CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): ends.");
+            return new KRAServerSideKeyGenResponse(response);
+        } else {
+            CMS.debug("KRARemoteRequestHandler: serverSideKeyGen(): no response content.");
+            throw new EBaseException("KRARemoteRequestHandler: serverSideKeyGen(): no response content.");
+        }
+
+    }
+
+    /**
+     * recoverKey recovers keys from KRA
+     *
+     * @param cuid is the token id
+     * @param userid is the user id
+     * @param sDesKey is the des key provided by the TKS for key encryption
+     * @param b64cert is the Base64 encoding of a certificate used to recover
+     *
+     * @returns KRARecoverKeyResponse
+     */
+    public KRARecoverKeyResponse recoverKey(
+            String cuid,
+            String userid,
+            String sDesKey,
+            String b64cert)
+            throws EBaseException {
+
+        CMS.debug("KRARemoteRequestHandler: recoverKey(): begins.");
+        if ((cuid == null) || (userid == null) || (sDesKey == null) || (b64cert == null)) {
+            throw new EBaseException("KRARemoteRequestHandler: recoverKey(): input parameter null.");
+        }
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("KRARemoteRequestHandler: recoverKey(): sending request to CA");
+
+        HttpResponse resp =
+                conn.send("TokenKeyRecovery",
+                        "&" + IRemoteRequest.TOKEN_CUID + "=" +
+                                cuid +
+                                "&" + IRemoteRequest.KRA_UserId + "=" +
+                                userid +
+                                "&" + IRemoteRequest.KRA_RECOVERY_CERT + "=" +
+                                b64cert +
+                                "&" + IRemoteRequest.KRA_Trans_DesKey + "=" +
+                                sDesKey);
+
+        String content = resp.getContent();
+
+        CMS.debug("KRARemoteRequestHandler: recoverKey(): got content = " + content);
+        if (content != null && !content.equals("")) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             * Note: response values "missing" might not be bad for some cases
+             */
+            String value = null;
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            value = (String) response.get(IRemoteRequest.RESPONSE_STATUS);
+
+            CMS.debug("KRARemoteRequestHandler: recoverKey(): got status = " + value);
+            ist = Integer.parseInt(value);
+            if (ist != 0) {
+                CMS.debug("KRARemoteRequestHandler: recoverKey(): status not 0, getting error string... ");
+                value = (String) response.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+                if (value == null) {
+                    CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " +
+                            IRemoteRequest.RESPONSE_ERROR_STRING);
+                } else {
+                    CMS.debug("KRARemoteRequestHandler: recoverKey(): got IRemoteRequest.RESPONSE_ERROR_STRING = "
+                            + value);
+                    response.put(IRemoteRequest.RESPONSE_ERROR_STRING, value);
+                }
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = (String) response.get(IRemoteRequest.KRA_RESPONSE_PublicKey);
+            if (value == null) {
+                CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " +
+                        IRemoteRequest.KRA_RESPONSE_PublicKey);
+            } else {
+                CMS.debug("KRARemoteRequestHandler:recoverKey(): got IRemoteRequest.KRA_RESPONSE_PublicKey= " + value);
+                response.put(IRemoteRequest.KRA_RESPONSE_PublicKey, value);
+            }
+
+            value = (String) response.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey);
+            if (value == null) {
+                CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " +
+                        IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey);
+            } else {
+                CMS.debug("KRARemoteRequestHandler:recoverKey(): got IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey= "
+                        + value);
+                response.put(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey, value);
+            }
+
+            value = (String) response.get(IRemoteRequest.KRA_RESPONSE_IV_Param);
+            if (value == null) {
+                CMS.debug("KRARemoteRequestHandler: recoverKey(): response missing name-value pair for: " +
+                        IRemoteRequest.KRA_RESPONSE_IV_Param);
+            } else {
+                CMS.debug("KRARemoteRequestHandler:recoverKey(): got IRemoteRequest.KRA_RESPONSE_IV_Param= " + value);
+                response.put(IRemoteRequest.KRA_RESPONSE_IV_Param, value);
+            }
+
+            CMS.debug("KRARemoteRequestHandler: recoverKey(): ends.");
+            return new KRARecoverKeyResponse(response);
+        } else {
+            CMS.debug("KRARemoteRequestHandler: recoverKey(): no response content.");
+            throw new EBaseException("KRARemoteRequestHandler: recoverKey(): no response content.");
+        }
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java
new file mode 100644
index 0000000..1836bcd
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/KRAServerSideKeyGenResponse.java
@@ -0,0 +1,51 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * KRAServerSideKeyGenResponse is the class for the response to
+ * KRA Remote Request: serverSideKeyGen()
+ *
+ */
+public class KRAServerSideKeyGenResponse extends RemoteResponse
+{
+    public KRAServerSideKeyGenResponse(Hashtable<String, Object> ht) {
+        nameValTable = ht;
+    }
+
+    public String getErrorString() {
+        return (String) nameValTable.get(IRemoteRequest.RESPONSE_ERROR_STRING);
+    }
+
+    public String getPublicKey() {
+        return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_PublicKey);
+    }
+
+    public String getWrappedPrivKey() {
+        return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_Wrapped_PrivKey);
+    }
+
+    public String getIVParam() {
+        return (String) nameValTable.get(IRemoteRequest.KRA_RESPONSE_IV_Param);
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteResponse.java
new file mode 100644
index 0000000..c2c7818
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteResponse.java
@@ -0,0 +1,40 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * RemoteResponse is the base class for remote requests
+ *
+ */
+public abstract class RemoteResponse
+{
+    protected Hashtable<String, Object> nameValTable;
+
+    public int getStatus() {
+        Integer iValue = (Integer) nameValTable.get(IRemoteRequest.RESPONSE_STATUS);
+        if (iValue == null)
+            return IRemoteRequest.RESPONSE_STATUS_NOT_FOUND;
+        else
+            return iValue.intValue();
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java
index e601238..3d53b93 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeRandomDataResponse.java
@@ -28,7 +28,7 @@ import org.dogtagpki.tps.main.TPSBuffer;
  * TKS Remote Request: computeRandomData()
  *
  */
-public class TKSComputeRandomDataResponse extends TKSResponse
+public class TKSComputeRandomDataResponse extends RemoteResponse
 {
     public TKSComputeRandomDataResponse(Hashtable<String, Object> ht) {
         nameValTable = ht;
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java
index 95d711f..7657c21 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSComputeSessionKeyResponse.java
@@ -28,7 +28,7 @@ import org.dogtagpki.tps.main.TPSBuffer;
  * TKS Remote Request: computeSessionKey()
  *
  */
-public class TKSComputeSessionKeyResponse extends TKSResponse
+public class TKSComputeSessionKeyResponse extends RemoteResponse
 {
 
     public TKSComputeSessionKeyResponse(Hashtable<String, Object> ht) {
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java
index 5178d62..f887c63 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSCreateKeySetDataResponse.java
@@ -28,7 +28,7 @@ import org.dogtagpki.tps.main.TPSBuffer;
  * TKS Remote Request: createKeySetData
  *
  */
-public class TKSCreateKeySetDataResponse extends TKSResponse
+public class TKSCreateKeySetDataResponse extends RemoteResponse
 {
 
     public TKSCreateKeySetDataResponse(Hashtable<String, Object> ht) {
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java
index e11d9e6..7dba171 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSEncryptDataResponse.java
@@ -28,7 +28,7 @@ import org.dogtagpki.tps.main.TPSBuffer;
  * TKS Remote Request: encryptData
  *
  */
-public class TKSEncryptDataResponse extends TKSResponse
+public class TKSEncryptDataResponse extends RemoteResponse
 {
 
     public TKSEncryptDataResponse(Hashtable<String, Object> ht) {
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
index 5aad056..0c8e057 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
@@ -41,7 +41,13 @@ import com.netscape.cmsutil.http.HttpResponse;
  */
 public class TKSRemoteRequestHandler extends RemoteRequestHandler
 {
-    public TKSRemoteRequestHandler(String connID) {
+    public TKSRemoteRequestHandler(String connID)
+            throws EBaseException {
+
+        if (connID == null) {
+            throw new EBaseException("TKSRemoteRequestHandler: TKSRemoteRequestHandler(): connID null.");
+        }
+
         connid = connID;
     }
 
@@ -69,7 +75,7 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
      * @return response TKSComputeSessionKeyResponse class object
      */
     public TKSComputeSessionKeyResponse computeSessionKey(
-            TPSBuffer cuid,
+            String cuid,
             TPSBuffer keyInfo,
             TPSBuffer card_challenge,
             TPSBuffer card_cryptogram,
@@ -77,10 +83,12 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
             throws EBaseException {
 
         CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): begins.");
+        if ((cuid == null) || (keyInfo == null) || (card_challenge == null)
+                || (card_cryptogram == null) || (host_challenge == null)) {
+            throw new EBaseException("TKSRemoteRequestHandler: computeSessionKey(): input parameter null.");
+        }
 
-        TPSSubsystem subsystem =
-                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
-        IConfigStore conf = subsystem.getConfigStore();
+        IConfigStore conf = CMS.getConfigStore();
 
         /*
          * TODO: obtain the profile name from current session when available.
@@ -92,13 +100,15 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
         String keySet =
                 conf.getString("connector." + connid + "keySet", "defKeySet");
 
-        CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): sending request to tks.");
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
         HttpConnector conn =
                 (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): sending request to tks.");
         HttpResponse resp =
                 conn.send("computeSessionKey",
                         IRemoteRequest.SERVER_SIDE_KEYGEN + "=" + serverKeygen +
-                                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialEncode(cuid) +
+                                "&" + IRemoteRequest.TOKEN_CUID + "=" + cuid +
                                 "&" + IRemoteRequest.TOKEN_CARD_CHALLENGE + "=" + Util.specialEncode(card_challenge) +
                                 "&" + IRemoteRequest.TOKEN_HOST_CHALLENGE + "=" + Util.specialEncode(host_challenge) +
                                 "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialEncode(keyInfo) +
@@ -213,22 +223,26 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
     public TKSCreateKeySetDataResponse createKeySetData(
             TPSBuffer NewMasterVer,
             TPSBuffer version,
-            TPSBuffer cuid)
+            String cuid)
             throws EBaseException {
         CMS.debug("TKSRemoteRequestHandler: createKeySetData(): begins.");
-        TPSSubsystem subsystem =
-                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
-        IConfigStore conf = subsystem.getConfigStore();
+        if ((cuid == null) || (NewMasterVer == null) || (version == null)) {
+            throw new EBaseException("TKSRemoteRequestHandler: createKeySetData(): input parameter null.");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
         String keySet =
                 conf.getString("connector." + connid + "keySet", "defKeySet");
 
-        CMS.debug("TKSRemoteRequestHandler: createKeySetData(): sending request to tks.");
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
         HttpConnector conn =
                 (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("TKSRemoteRequestHandler: createKeySetData(): sending request to tks.");
         HttpResponse resp =
                 conn.send("createKeySetData",
                         IRemoteRequest.TOKEN_NEW_KEYINFO + "=" + Util.specialEncode(NewMasterVer) +
-                                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialEncode(cuid) +
+                                "&" + IRemoteRequest.TOKEN_CUID + "=" + cuid +
                                 "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialEncode(version) +
                                 "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet);
 
@@ -367,26 +381,29 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
      * @return response TKSEncryptDataResponse class object
      */
     public TKSEncryptDataResponse encryptData(
-            TPSBuffer cuid,
+            String cuid,
             TPSBuffer version,
             TPSBuffer inData)
             throws EBaseException {
         CMS.debug("TKSRemoteRequestHandler: encryptData(): begins.");
+        if ((cuid == null) || (version == null) || (inData == null)) {
+            throw new EBaseException("TKSRemoteRequestHandler: encryptData(): input parameter null.");
+        }
 
-        TPSSubsystem subsystem =
-                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
-        IConfigStore conf = subsystem.getConfigStore();
+        IConfigStore conf = CMS.getConfigStore();
 
         String keySet =
                 conf.getString("connector." + connid + "keySet", "defKeySet");
 
-        CMS.debug("TKSRemoteRequestHandler: encryptData(): sending request to tks.");
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
         HttpConnector conn =
                 (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("TKSRemoteRequestHandler: encryptData(): sending request to tks.");
         HttpResponse resp =
                 conn.send("encryptData",
                         IRemoteRequest.TOKEN_DATA + "=" + Util.specialEncode(inData) +
-                                "&" + IRemoteRequest.TOKEN_CUID + "=" + Util.specialEncode(cuid) +
+                                "&" + IRemoteRequest.TOKEN_CUID + "=" + cuid +
                                 "&" + IRemoteRequest.TOKEN_KEYINFO + "=" + Util.specialEncode(version) +
                                 "&" + IRemoteRequest.TOKEN_KEYSET + "=" + keySet);
 
-- 
1.8.4.2

