>From 3ac967a6cbc703f1233d357a8a36742da3e7e765 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Mon, 10 Mar 2014 14:46:14 -0700
Subject: [PATCH] Ticket #888 - TPS rewrite: provide remote authority functions
 (patch 1: TKS functions)

---
 base/common/src/org/dogtagpki/tps/main/Util.java   |  11 +
 .../server/tps/cms/ConnectionManager.java          |  22 --
 .../server/tps/cms/RemoteRequestHandler.java       |  87 +++++
 .../server/tps/cms/TKSRemoteRequestHandler.java    | 395 +++++++++++++++++++++
 4 files changed, 493 insertions(+), 22 deletions(-)
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java

diff --git a/base/common/src/org/dogtagpki/tps/main/Util.java b/base/common/src/org/dogtagpki/tps/main/Util.java
index a493dd6..aba6c6e 100644
--- a/base/common/src/org/dogtagpki/tps/main/Util.java
+++ b/base/common/src/org/dogtagpki/tps/main/Util.java
@@ -24,6 +24,8 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 
+import com.netscape.cmsutil.util.Utils;
+
 
 public class Util {
 
@@ -125,4 +127,13 @@ public class Util {
         return result.toString();
     }
 
+    public static String specialEncode(TPSBuffer data) {
+        return Utils.SpecialEncode(data.toBytesArray());
+    }
+
+    public static TPSBuffer specialDecode(String str) {
+        byte[] data = Utils.SpecialDecode(str);
+        TPSBuffer tbuf = new TPSBuffer(data);
+        return tbuf;
+    }
 }
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 c9b88f1..e0c585b 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
@@ -201,26 +201,4 @@ public class ConnectionManager
         return connectors.get(connID);
     }
 
-    /*
-     * Get the XML parser for XML in text
-     *
-     * @param text XML in text
-     * @return XMLObject the parser
-     */
-    public XMLObject getXMLParser(String text) {
-        if (text == null) {
-            return null;
-        } else {
-            CMS.debug("ConnectionManager: getXMLparser(): parsing: "+ text);
-        }
-        try {
-            ByteArrayInputStream bis =
-                new ByteArrayInputStream(text.getBytes());
-            return new XMLObject(bis);
-        } catch (Exception e) {
-            CMS.debug("ConnectionManager: getXMLparser(): failed: "+ e);
-            throw new RuntimeException(e);
-        }
-    }
-
 }
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java
new file mode 100644
index 0000000..f545580
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/RemoteRequestHandler.java
@@ -0,0 +1,87 @@
+// --- 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) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.io.ByteArrayInputStream;
+import java.util.Hashtable;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.cmsutil.xml.XMLObject;
+
+/**
+ * RemoteRequestHandler is the base class for the remote authorities
+ *
+ * @author cfu
+ */
+public class RemoteRequestHandler
+{
+    public static String RESPONSE_SEPARATOR = "\\&";
+    public static String NAME_VALUE_EQUAL = "=";
+
+    protected String connid;
+
+    /**
+     * parseResponse parses remote responses that take the form of '&'-separated
+     * name-value pairs
+     *
+     * @param content The exact string content in the HTTP response
+     * @return name-value pairs in a Hashtable
+     */
+    public Hashtable<String, Object> parseResponse(String content) {
+        CMS.debug("RemoteRequestHandler: parseResponse(): begins:");
+        if (content == null) {
+            return null;
+        }
+        Hashtable<String, Object> vars = new Hashtable<String, Object>();
+        String[] elements = content.split(RESPONSE_SEPARATOR);
+        CMS.debug("RemoteRequestHandler: parseResponse(): # of elements:" +
+            elements.length);
+        for (String e : elements) {
+            String nvs = new String(e);
+            String[] nv = nvs.split(NAME_VALUE_EQUAL);
+            if (nv[0] != null && nv[1] != null) {
+                vars.put(nv[0], nv[1]);
+            }
+        }
+        return vars;
+    }
+
+    /**
+     * Get the XML parser for XML in text
+     *
+     * @param text XML in text
+     * @return XMLObject the parser
+     */
+    public XMLObject getXMLparser(String text) {
+        if (text == null) {
+            return null;
+        } else {
+            CMS.debug("RemoteRequestHandler: getXMLparser(): parsing: " + text);
+        }
+        try {
+            ByteArrayInputStream bis =
+                    new ByteArrayInputStream(text.getBytes());
+            return new XMLObject(bis);
+        } catch (Exception e) {
+            CMS.debug("RemoteRequestHandler: getXMLparser(): failed: " + e);
+            throw new RuntimeException(e);
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..721b1ad
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
@@ -0,0 +1,395 @@
+// --- 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) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import org.dogtagpki.server.tps.TPSSubsystem;
+import org.dogtagpki.tps.main.TPSBuffer;
+import org.dogtagpki.tps.main.Util;
+
+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;
+
+/**
+ * TKSRemoteRequestHandler is a class representing remote requests
+ * offered by the Token Key Service Authority (TKS)
+ * On a successful return, name/value pairs are provided in a Hashtable where
+ * all contents are URL decoded if needed
+ *
+ * @author cfu
+ */
+public class TKSRemoteRequestHandler extends RemoteRequestHandler
+{
+    public static String TKS_RESPONSE_STATUS = "status";
+
+    /* computeSessionKey responses */
+    public static String TKS_RESPONSE_SessionKey = "sessionKey";
+    public static String TKS_RESPONSE_EncSessionKey = "encSessionKey";
+    public static String TKS_RESPONSE_KEK_DesKey = "kek_wrapped_desKey";
+    public static String TKS_RESPONSE_DRM_Trans_DesKey = "drm_trans_wrapped_desKey";
+    public static String TKS_RESPONSE_KeyCheck = "keycheck";
+    public static String TKS_RESPONSE_HostCryptogram = "hostCryptogram";
+
+    /* createKeySetData response */
+    public static String TKS_RESPONSE_KeySetData = "keySetData";
+
+    /* encryptData response */
+    public static String TKS_RESPONSE_EncryptedData = "encryptedData";
+
+    /* computeRandomData response */
+    public static String TKS_RESPONSE_RandomData = "DATA";
+
+    public TKSRemoteRequestHandler(String connID) {
+        connid = connID;
+    }
+
+    /*
+     * computeSessionKey
+     *
+     * Usage Example:
+     *   TKSRemoteRequestHandler tksReq = new TKSRemoteRequestHandler("tks1");
+     *   Hashtable<String,String> responseObj = tksReq.computeSessionKey(
+     *      cuid,
+     *      keyInfo,
+     *      card_challenge,
+     *      card_cryptogram,
+     *      host_challenge);
+     *   - on success return, one can say
+     *    String value = responseObj.getValue(param);
+     *      to get response param values
+     *
+     * @param cuid
+     * @param keyInfo
+     * @param card_challenge
+     * @param card_cryptogram
+     * @param host_challenge
+     * @return response a Hashtable containing name-value pairs
+     */
+    public Hashtable<String, Object> computeSessionKey(
+            TPSBuffer cuid,
+            TPSBuffer keyInfo,
+            TPSBuffer card_challenge,
+            TPSBuffer card_cryptogram,
+            TPSBuffer host_challenge)
+            throws EBaseException {
+
+        CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): begins.");
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        IConfigStore conf = subsystem.getConfigStore();
+
+        /*
+         * TODO: obtain the profile name from current session when available.
+         *   Hard-code to "userKey" for now
+         */
+        boolean serverKeygen =
+                conf.getBoolean("op.enroll.userKey.keyGen.encryption.serverKeygen.enable",
+                        false);
+        String keySet =
+                conf.getString("connector." + connid + "keySet", "defKeySet");
+
+        CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): sending request to tks.");
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        HttpResponse resp =
+                conn.send("computeSessionKey",
+                        "serversideKeygen=" + serverKeygen +
+                                "&CUID=" + Util.specialEncode(cuid) +
+                                "&card_challenge=" + Util.specialEncode(card_challenge) +
+                                "&host_challenge=" + Util.specialEncode(host_challenge) +
+                                "&KeyInfo=" + Util.specialEncode(keyInfo) +
+                                "&card_cryptogram=" + Util.specialEncode(card_cryptogram) +
+                                "&keySet=" + keySet);
+
+        String content = resp.getContent();
+
+        if ((content != null) && (!content.equals(""))) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+            if (response == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): parseResponse returned null.");
+                return null;
+            }
+
+            /*
+             * 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;
+            value = (String) response.get(TKS_RESPONSE_STATUS);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): Bad TKS Connection. Please make sure TKS is accessible by TPS.");
+                return null;
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got status =" + value);
+            }
+
+            value = (String) response.get(TKS_RESPONSE_SessionKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " +
+                        TKS_RESPONSE_SessionKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got TKS_RESPONSE_SessionKey");
+                response.put(TKS_RESPONSE_SessionKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(TKS_RESPONSE_EncSessionKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " +
+                        TKS_RESPONSE_EncSessionKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got TKS_RESPONSE_EncSessionKey");
+                response.put(TKS_RESPONSE_EncSessionKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(TKS_RESPONSE_DRM_Trans_DesKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " +
+                        TKS_RESPONSE_DRM_Trans_DesKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got TKS_RESPONSE_DRM_Trans_DesKey");
+                response.put(TKS_RESPONSE_DRM_Trans_DesKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(TKS_RESPONSE_KEK_DesKey);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " +
+                        TKS_RESPONSE_KEK_DesKey);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got TKS_RESPONSE_KEK_DesKey");
+                response.put(TKS_RESPONSE_KEK_DesKey, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(TKS_RESPONSE_KeyCheck);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " +
+                        TKS_RESPONSE_KeyCheck);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got TKS_RESPONSE_KeyCheck");
+                response.put(TKS_RESPONSE_KeyCheck, Util.specialDecode(value));
+            }
+
+            value = (String) response.get(TKS_RESPONSE_HostCryptogram);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): response missing name-value pair for: " +
+                        TKS_RESPONSE_HostCryptogram);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): got TKS_RESPONSE_HostCryptogram");
+                response.put(TKS_RESPONSE_HostCryptogram, Util.specialDecode(value));
+            }
+            CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): ends.");
+
+            return response;
+        } else {
+            CMS.debug("TKSRemoteRequestHandler: computeSessionKey(): no response content.");
+            return null;
+        }
+    }
+
+    public Hashtable<String, Object> createKeySetData(
+            TPSBuffer NewMasterVer,
+            TPSBuffer version,
+            TPSBuffer cuid)
+            throws EBaseException {
+        CMS.debug("TKSRemoteRequestHandler: createKeySetData(): begins.");
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        IConfigStore conf = subsystem.getConfigStore();
+        String keySet =
+                conf.getString("connector." + connid + "keySet", "defKeySet");
+
+        CMS.debug("TKSRemoteRequestHandler: createKeySetData(): sending request to tks.");
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        HttpResponse resp =
+                conn.send("createKeySetData",
+                        "newKeyInfo=" + Util.specialEncode(NewMasterVer) +
+                                "&CUID=" + Util.specialEncode(cuid) +
+                                "&KeyInfo" + Util.specialEncode(version) +
+                                "&keySet=" + keySet);
+
+        String content = resp.getContent();
+
+        if ((content != null) && (!content.equals(""))) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+            if (response == null) {
+                CMS.debug("TKSRemoteRequestHandler: createKeySetData(): parseResponse returned null.");
+                return null;
+            }
+
+            /*
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             */
+            String value = null;
+            value = (String) response.get(TKS_RESPONSE_STATUS);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: createKeySetData(): Bad TKS Connection. Please make sure TKS is accessible by TPS.");
+                return null;
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: createKeySetData(): got status =" + value);
+            }
+            value = (String) response.get(TKS_RESPONSE_KeySetData);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: createKeySetData(): response missing name-value pair for: " +
+                        TKS_RESPONSE_KeySetData);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: createKeySetData(): got TKS_RESPONSE_KeySetData");
+                response.put(TKS_RESPONSE_KeySetData, Util.specialDecode(value));
+            }
+            CMS.debug("TKSRemoteRequestHandler: createKeySetData(): ends.");
+
+            return response;
+        } else {
+            CMS.debug("TKSRemoteRequestHandler: createKeySetData(): no response content.");
+            return null;
+        }
+    }
+
+    public Hashtable<String, Object> computeRandomData(int dataSize)
+            throws EBaseException {
+        CMS.debug("TKSRemoteRequestHandler: computeRandomData(): begins.");
+        /*
+         * check for absurd dataSize values
+         */
+        if (dataSize <= 0 || dataSize > 1024) {
+            CMS.debug("TKSRemoteRequestHandler: computeRandomData(): invalid dataSize requested:" + dataSize);
+            throw new EBaseException("TKSRemoteRequestHandler: computeRandomData(): invalid dataSize requested");
+        }
+        CMS.debug("TKSRemoteRequestHandler: computeRandomData(): sending request to tks.");
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        HttpResponse resp =
+                conn.send("computeRandomData",
+                        "dataNumBytes=" + dataSize);
+
+        String content = resp.getContent();
+
+        if ((content != null) && (!content.equals(""))) {
+            // CMS.debug("TKSRemoteRequestHandler: computeRandomData(): got content =" + content);
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+            if (response == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeRandomData(): parseResponse returned null.");
+                return null;
+            }
+
+            /*
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             */
+            String value = null;
+            value = (String) response.get(TKS_RESPONSE_STATUS);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeRandomData(): Bad TKS Connection. Please make sure TKS is accessible by TPS.");
+                return null;
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeRandomData(): got status =" + value);
+            }
+            value = (String) response.get(TKS_RESPONSE_RandomData);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: computeRandomData(): response missing name-value pair for: " +
+                        TKS_RESPONSE_RandomData);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: computeRandomData(): got TKS_RESPONSE_RandomData" + value);
+                response.put(TKS_RESPONSE_RandomData, Util.specialDecode(value));
+            }
+            CMS.debug("TKSRemoteRequestHandler: computeRandomData(): ends.");
+
+            return response;
+        } else {
+            CMS.debug("TKSRemoteRequestHandler: computeRandomData(): no response content.");
+            return null;
+        }
+    }
+
+    public Hashtable<String, Object> encryptData(
+            TPSBuffer cuid,
+            TPSBuffer version,
+            TPSBuffer inData)
+            throws EBaseException {
+        CMS.debug("TKSRemoteRequestHandler: encryptData(): begins.");
+
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        IConfigStore conf = subsystem.getConfigStore();
+
+        String keySet =
+                conf.getString("connector." + connid + "keySet", "defKeySet");
+
+        CMS.debug("TKSRemoteRequestHandler: encryptData(): sending request to tks.");
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        HttpResponse resp =
+                conn.send("encryptData",
+                        "data=" + Util.specialEncode(inData) +
+                                "&CUID=" + Util.specialEncode(cuid) +
+                                "&KeyInfo=" + Util.specialEncode(version) +
+                                "&keySet=" + keySet);
+
+        String content = resp.getContent();
+
+        if ((content != null) && (!content.equals(""))) {
+            Hashtable<String, Object> response =
+                    parseResponse(content);
+            if (response == null) {
+                CMS.debug("TKSRemoteRequestHandler: encryptData(): parseResponse returned null.");
+                return null;
+            }
+
+            /*
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             */
+            String value = null;
+            value = (String) response.get(TKS_RESPONSE_STATUS);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: encryptData(): Bad TKS Connection. Please make sure TKS is accessible by TPS.");
+                return null;
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: encryptData(): got status =" + value);
+            }
+
+            value = (String) response.get(TKS_RESPONSE_EncryptedData);
+            if (value == null) {
+                CMS.debug("TKSRemoteRequestHandler: encryptData(): response missing name-value pair for: " +
+                        TKS_RESPONSE_EncryptedData);
+            } else {
+                CMS.debug("TKSRemoteRequestHandler: encryptData(): got TKS_RESPONSE_EncryptedData");
+                response.put(TKS_RESPONSE_EncryptedData, Util.specialDecode(value));
+            }
+            CMS.debug("TKSRemoteRequestHandler: encryptData(): ends.");
+
+            return response;
+        } else {
+            CMS.debug("TKSRemoteRequestHandler: encryptData(): no response content.");
+            return null;
+        }
+    }
+}
-- 
1.8.4.2

