>From 02e204d3fb425db8e5c061afcd5b8fb8f3f3f3a3 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 25 Sep 2013 22:09:10 -0400
Subject: [PATCH 159/160] Add service to generate and retrieve a shared secret

A new REST service has been added to the TKS to manage shared secrets.
The shared secret is tied to the TKS-TPS connector, and is created at the
end of the TPS configuration.  At this point, the TPS contacts the TKS and
requests that the shared secret be generated.  The secret is returned to the
TPS, wrapped using the subsystem certificate of the TPS.

The TPS should then decrypt the shared secret and store it in its certificate
database.  This operations requires JSS changes, though, and so will be deferred
to a later patch.  For now, though, if the TPS and TKS share the same certdb, then
it is sufficient to generate the shared secret.

Clients and CLI are also provided.  The CLI in particular is used to remove the
TPSConnector entries and the shared secret when the TPS is pkidestroyed.
---
 .../certsrv/system/ConfigurationRequest.java       |  15 +
 .../certsrv/system/TPSConnectorClient.java         |  66 ++++
 .../certsrv/system/TPSConnectorCollection.java     |  17 +
 .../netscape/certsrv/system/TPSConnectorData.java  | 153 +++++++++
 .../certsrv/system/TPSConnectorResource.java       |  91 +++++
 .../src/com/netscape/certsrv/tks/TKSClient.java    |   2 +
 .../cms/servlet/csadmin/ConfigurationUtils.java    |  62 +++-
 .../cms/servlet/csadmin/SystemConfigService.java   |  13 +
 .../cms/servlet/tks/TPSConnectorService.java       | 376 +++++++++++++++++++++
 .../src/com/netscape/cmstools/cli/TKSCLI.java      |   2 +
 .../cmstools/system/TPSConnectorAddCLI.java        |  52 +++
 .../netscape/cmstools/system/TPSConnectorCLI.java  |  94 ++++++
 .../cmstools/system/TPSConnectorFindCLI.java       |  88 +++++
 .../cmstools/system/TPSConnectorRemoveCLI.java     |  53 +++
 base/server/etc/default.cfg                        |   1 +
 .../python/pki/server/deployment/pkihelper.py      | 125 +++++++
 .../python/pki/server/deployment/pkimessages.py    |   5 +
 .../server/deployment/scriptlets/initialization.py |   3 +
 base/tks/src/com/netscape/tks/TKSApplication.java  |   4 +
 .../com/netscape/cmsutil/crypto/CryptoUtil.java    |  78 ++++-
 20 files changed, 1298 insertions(+), 2 deletions(-)
 create mode 100644 base/common/src/com/netscape/certsrv/system/TPSConnectorClient.java
 create mode 100644 base/common/src/com/netscape/certsrv/system/TPSConnectorCollection.java
 create mode 100644 base/common/src/com/netscape/certsrv/system/TPSConnectorData.java
 create mode 100644 base/common/src/com/netscape/certsrv/system/TPSConnectorResource.java
 create mode 100644 base/common/src/com/netscape/cms/servlet/tks/TPSConnectorService.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/TPSConnectorAddCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/TPSConnectorCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/TPSConnectorFindCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/TPSConnectorRemoveCLI.java

diff --git a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
index 23021a5731e57885380368e3075a99e9abb296f6..4fdf594d1968590ff031b7e3025a3690f012908a 100644
--- a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
+++ b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
@@ -84,6 +84,8 @@ public class ConfigurationRequest {
     private static final String KRA_URI = "kraUri";
     private static final String ENABLE_SERVER_SIDE_KEYGEN = "enableServerSideKeygen";
 
+    // TKS/TPS shared secret parameters
+    private static final String IMPORT_SHARED_SECRET = "importSharedSecret";
 
     //defaults
     public static final String TOKEN_DEFAULT = "Internal Key Storage Token";
@@ -243,6 +245,9 @@ public class ConfigurationRequest {
     @XmlElement(defaultValue="false")
     protected String enableServerSideKeyGen;
 
+    @XmlElement(defaultValue="false")
+    protected String importSharedSecret;
+
     public ConfigurationRequest() {
         // required for JAXB
     }
@@ -298,6 +303,7 @@ public class ConfigurationRequest {
         tksUri = form.getFirst(TKS_URI);
         kraUri = form.getFirst(KRA_URI);
         enableServerSideKeyGen = form.getFirst(ENABLE_SERVER_SIDE_KEYGEN);
+        importSharedSecret = form.getFirst(IMPORT_SHARED_SECRET);
     }
 
     public String getSubsystemName() {
@@ -878,6 +884,14 @@ public class ConfigurationRequest {
         this.enableServerSideKeyGen = enableServerSideKeyGen;
     }
 
+    public String getImportSharedSecret() {
+        return importSharedSecret;
+    }
+
+    public void setImportSharedSecret(String importSharedSecret) {
+        this.importSharedSecret = importSharedSecret;
+    }
+
     @Override
     public String toString() {
         return "ConfigurationRequest [pin=XXXX" +
@@ -930,6 +944,7 @@ public class ConfigurationRequest {
                ", kraUri=" + kraUri +
                ", tksUri=" + tksUri +
                ", enableServerSideKeyGen=" + enableServerSideKeyGen +
+               ", importSharedSecret=" + importSharedSecret +
                "]";
     }
 }
diff --git a/base/common/src/com/netscape/certsrv/system/TPSConnectorClient.java b/base/common/src/com/netscape/certsrv/system/TPSConnectorClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..09ba9db45a96f88b08684d1a7f767180679ea20f
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/TPSConnectorClient.java
@@ -0,0 +1,66 @@
+package com.netscape.certsrv.system;
+
+import java.net.URISyntaxException;
+
+import com.netscape.certsrv.client.Client;
+import com.netscape.certsrv.client.PKIClient;
+import com.netscape.certsrv.key.KeyData;
+
+public class TPSConnectorClient extends Client {
+
+    private TPSConnectorResource tpsConnectorClient;
+
+    public TPSConnectorClient(PKIClient client) throws URISyntaxException {
+        this(client, client.getSubsystem());
+    }
+
+    public TPSConnectorClient(PKIClient client, String subsystem) throws URISyntaxException {
+        super(client, subsystem, "tpsconnector");
+        init();
+    }
+
+    public void init() throws URISyntaxException {
+        tpsConnectorClient = createProxy(TPSConnectorResource.class);
+    }
+
+    public TPSConnectorCollection listConnectors() {
+        return tpsConnectorClient.listConnectors();
+    }
+
+    public TPSConnectorData getConnector(String id) {
+        return tpsConnectorClient.getConnector(id);
+    }
+
+    public TPSConnectorData getConnector(String host, String port) {
+        return tpsConnectorClient.getConnector(host, port);
+    }
+
+    public TPSConnectorData createConnector(String tpsHost, String tpsPort) {
+        return tpsConnectorClient.createConnector(tpsHost, tpsPort);
+    }
+
+    public void deleteConnector(String id) {
+        tpsConnectorClient.deleteConnector(id);
+    }
+
+    public KeyData createSharedSecret(String id) {
+        return tpsConnectorClient.createSharedSecret(id);
+    }
+
+    public KeyData replaceSharedSecret(String id) {
+        return tpsConnectorClient.replaceSharedSecret(id);
+    };
+
+    public void deleteSharedSecret(String id) {
+        tpsConnectorClient.deleteSharedSecret(id);
+    }
+
+    public KeyData getSharedSecret(String id) {
+        return tpsConnectorClient.getSharedSecret(id);
+    }
+
+    public void deleteConnector(String host, String port) {
+        tpsConnectorClient.deleteConnector(host, port);
+    }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/system/TPSConnectorCollection.java b/base/common/src/com/netscape/certsrv/system/TPSConnectorCollection.java
new file mode 100644
index 0000000000000000000000000000000000000000..106a681bf9caa9989ed5506d28f6b39091688989
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/TPSConnectorCollection.java
@@ -0,0 +1,17 @@
+package com.netscape.certsrv.system;
+
+import java.util.Collection;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.netscape.certsrv.base.DataCollection;
+
+@XmlRootElement(name="TPSConnectors")
+public class TPSConnectorCollection extends DataCollection<TPSConnectorData> {
+
+    @XmlElementRef
+    public Collection<TPSConnectorData> getEntries() {
+        return super.getEntries();
+    }
+}
diff --git a/base/common/src/com/netscape/certsrv/system/TPSConnectorData.java b/base/common/src/com/netscape/certsrv/system/TPSConnectorData.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5e1480eb5961ac4c3f68a293c26b35d4fa16406
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/TPSConnectorData.java
@@ -0,0 +1,153 @@
+package com.netscape.certsrv.system;
+
+import java.io.StringReader;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+@XmlRootElement(name="TPSSystemClient")
+public class TPSConnectorData {
+
+    public static Marshaller marshaller;
+    public static Unmarshaller unmarshaller;
+
+    static {
+        try {
+            marshaller = JAXBContext.newInstance(TPSConnectorData.class).createMarshaller();
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+            unmarshaller = JAXBContext.newInstance(TPSConnectorData.class).createUnmarshaller();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    String id;
+    String host;
+    String port;
+    String userID;
+    String nickname;
+
+    Link link;
+
+    @XmlAttribute(name="id")
+    public String getID() {
+        return id;
+    }
+
+    public void setID(String id) {
+        this.id = id;
+    }
+
+    @XmlAttribute(name="Host")
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    @XmlAttribute(name="Port")
+    public String getPort() {
+        return port;
+    }
+
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    @XmlElement(name="UserID")
+    public String getUserID() {
+        return userID;
+    }
+
+    public void setUserID(String userID) {
+        this.userID = userID;
+    }
+
+    @XmlAttribute(name="Nickname")
+    public String getNickname() {
+        return nickname;
+    }
+
+    public void setNickname(String nickname) {
+        this.nickname = nickname;
+    }
+
+    public Link getLink() {
+        return link;
+    }
+
+    public void setLink(Link link) {
+        this.link = link;
+    }
+
+    public static TPSConnectorData valueOf(String string) throws Exception {
+        try {
+            return (TPSConnectorData)unmarshaller.unmarshal(new StringReader(string));
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "TPSSystemClientData [id=" + id + ", host=" + host + ", port=" + port +
+                ", userID=" + userID + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((host == null) ? 0 : host.hashCode());
+        result = prime * result + ((port == null) ? 0 : port.hashCode());
+        result = prime * result + ((userID == null) ? 0 : userID.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TPSConnectorData other = (TPSConnectorData) obj;
+        if (host == null) {
+            if (other.host != null)
+                return false;
+        } else if (!host.equals(other.host))
+            return false;
+        if (port == null) {
+            if (other.port != null)
+                return false;
+        } else if (!port.equals(other.port))
+            return false;
+        if (userID == null) {
+            if (other.userID != null)
+                return false;
+        } else if (!userID.equals(other.userID))
+            return false;
+        return true;
+    }
+
+    public static void main(String args[]) throws Exception {
+        TPSConnectorData before = new TPSConnectorData();
+        before.setID("tps0");
+        before.setUserID("user1");
+
+        String string = before.toString();
+        System.out.println(string);
+
+        TPSConnectorData after = TPSConnectorData.valueOf(string);
+        System.out.println(before.equals(after));
+    }
+}
diff --git a/base/common/src/com/netscape/certsrv/system/TPSConnectorResource.java b/base/common/src/com/netscape/certsrv/system/TPSConnectorResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a76ee696d9570ba5a2aa20baba4bb7828ef45b4
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/TPSConnectorResource.java
@@ -0,0 +1,91 @@
+// --- 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 com.netscape.certsrv.system;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import com.netscape.certsrv.acls.ACLMapping;
+import com.netscape.certsrv.authentication.AuthMethodMapping;
+import com.netscape.certsrv.key.KeyData;
+
+/**
+ * @author Ade Lee
+ */
+@Path("/admin/tps-connectors")
+@AuthMethodMapping("admin")
+public interface TPSConnectorResource {
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public TPSConnectorCollection listConnectors();
+
+    @GET
+    @Path("{id}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public TPSConnectorData getConnector(@PathParam("id") String id);
+
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public TPSConnectorData getConnector(@QueryParam("host") String host,
+            @QueryParam("port") String port);
+
+    @POST
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public TPSConnectorData createConnector(@QueryParam("host") String host,
+            @QueryParam("port") String port);
+
+    @DELETE
+    @Path("{id}")
+    public void deleteConnector(@PathParam("id") String id);
+
+    @POST
+    @Path("{id}/shared-secret")
+    @ACLMapping("admin.sharedsecret")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public KeyData createSharedSecret(@PathParam("id") String id);
+
+    @PUT
+    @Path("{id}/shared-secret")
+    @ACLMapping("admin.sharedsecret")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public KeyData replaceSharedSecret(@PathParam("id") String id);
+
+    @DELETE
+    @Path("{id}/shared-secret")
+    @ACLMapping("admin.sharedsecret")
+    public void deleteSharedSecret(@PathParam("id") String id);
+
+    @DELETE
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public void deleteConnector(@QueryParam("host") String host,
+            @QueryParam("port") String port);
+
+    @GET
+    @Path("{id}/shared-secret")
+    @ACLMapping("admin.sharedsecret")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public KeyData getSharedSecret(@PathParam("id") String id);
+}
diff --git a/base/common/src/com/netscape/certsrv/tks/TKSClient.java b/base/common/src/com/netscape/certsrv/tks/TKSClient.java
index baecf8ddbae1cda183d49e8d7a6ca2db5208e131..a13089f865c6b70ad1f485ed764f93816a26d1bb 100644
--- a/base/common/src/com/netscape/certsrv/tks/TKSClient.java
+++ b/base/common/src/com/netscape/certsrv/tks/TKSClient.java
@@ -22,6 +22,7 @@ import java.net.URISyntaxException;
 import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.client.SubsystemClient;
 import com.netscape.certsrv.group.GroupClient;
+import com.netscape.certsrv.system.TPSConnectorClient;
 import com.netscape.certsrv.user.UserClient;
 
 public class TKSClient extends SubsystemClient {
@@ -34,5 +35,6 @@ public class TKSClient extends SubsystemClient {
     public void init() throws URISyntaxException {
         addClient(new GroupClient(client, name));
         addClient(new UserClient(client, name));
+        addClient(new TPSConnectorClient(client, name));
     }
 }
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/common/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
index 78d14b3f50666691bd09992acf6b58dcd082fb53..1e6ee25a3b5487e261c6c69eadfaa17270bee42a 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
@@ -147,11 +147,14 @@ import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.client.PKIConnection;
 import com.netscape.certsrv.dbs.IDBSubsystem;
 import com.netscape.certsrv.dbs.crldb.ICRLIssuingPointRecord;
+import com.netscape.certsrv.key.KeyData;
 import com.netscape.certsrv.ldap.ILdapConnFactory;
 import com.netscape.certsrv.ocsp.IDefStore;
 import com.netscape.certsrv.ocsp.IOCSPAuthority;
 import com.netscape.certsrv.system.InstallToken;
 import com.netscape.certsrv.system.SecurityDomainClient;
+import com.netscape.certsrv.system.TPSConnectorClient;
+import com.netscape.certsrv.system.TPSConnectorData;
 import com.netscape.certsrv.usrgrp.EUsrGrpException;
 import com.netscape.certsrv.usrgrp.IGroup;
 import com.netscape.certsrv.usrgrp.IUGSubsystem;
@@ -3568,6 +3571,63 @@ public class ConfigurationUtils {
         return null;
     }
 
+    public static void getSharedSecret(String tksHost, int tksPort, boolean importKey) throws EPropertyNotFound,
+            EBaseException, URISyntaxException {
+        IConfigStore cs = CMS.getConfigStore();
+        String host = cs.getString("service.machineName");
+        String port = cs.getString("service.securePort");
+        String dbDir = cs.getString("instanceRoot") + "/alias";
+        String dbNick = cs.getString("tps.cert.subsystem.nickname");
+
+        String passwordFile = cs.getString("passwordFile");
+        IConfigStore psStore = CMS.createFileConfigStore(passwordFile);
+        String dbPass = psStore.getString("internal");
+
+        ClientConfig config = new ClientConfig();
+        config.setServerURI("https://" + tksHost + ":" + tksPort + "/tks");
+        config.setCertDatabase(dbDir);
+        config.setCertNickname(dbNick);
+        config.setCertPassword(dbPass);
+
+        PKIClient client = new PKIClient(config);
+        PKIConnection connection = client.getConnection();
+
+        // Ignore the "UNTRUSTED_ISSUER" and "CA_CERT_INVALID" validity status
+        // during PKI instance creation since we are using an untrusted temporary CA cert.
+        connection.addIgnoredCertStatus(SSLCertificateApprovalCallback.ValidityStatus.UNTRUSTED_ISSUER);
+        connection.addIgnoredCertStatus(SSLCertificateApprovalCallback.ValidityStatus.CA_CERT_INVALID);
+
+        AccountClient accountClient = new AccountClient(client);
+        TPSConnectorClient tpsConnectorClient = new TPSConnectorClient(client);
+
+        accountClient.login();
+        TPSConnectorData data = tpsConnectorClient.getConnector(host, port);
+        KeyData keyData = null;
+        if (data == null) {
+            data = tpsConnectorClient.createConnector(host, port);
+            keyData = tpsConnectorClient.createSharedSecret(data.getID());
+        } else {
+            String connId = data.getID();
+            keyData = tpsConnectorClient.getSharedSecret(connId);
+            if (keyData != null) {
+                keyData = tpsConnectorClient.replaceSharedSecret(connId);
+            } else {
+                keyData = tpsConnectorClient.createSharedSecret(connId);
+            }
+        }
+        accountClient.logout();
+
+        if (importKey) {
+            // TODO - we need code here to import the key into the tps certdb
+            // this is not needed if we are using a shared database with
+            // the tks.
+        }
+
+        // store the new nick in CS.cfg
+        String nick = "TPS-" + host + "-" + port + " sharedSecret";
+        cs.putString("conn.tks1.tksSharedSymKeyName", nick);
+        cs.commit(false);
+    }
 
     public static void importCACertToOCSP() throws IOException, EBaseException, CertificateEncodingException {
         IConfigStore config = CMS.getConfigStore();
@@ -3729,7 +3789,7 @@ public class ConfigurationUtils {
             CMS.debug("registerUser: status=" + status);
 
             if (status.equals(SUCCESS)) {
-                CMS.debug("registerUser: Successfully added user " + uid + " to " + targetURI + 
+                CMS.debug("registerUser: Successfully added user " + uid + " to " + targetURI +
                           " using " + targetURL);
             } else if (status.equals(AUTH_FAILURE)) {
                 throw new EAuthException(AUTH_FAILURE);
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java b/base/common/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
index 1884ca3d28d612de14d3eced1a3008ce35a356be..fbadc80d3c4c570a015e9a46a8de07ea1e6ad9fb 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
@@ -504,6 +504,13 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                     String transportCert = ConfigurationUtils.getTransportCert(secdomainURI, kraURI);
                     ConfigurationUtils.exportTransportCert(secdomainURI, tksURI, transportCert);
                 }
+
+                // generate shared secret from the tks
+                ConfigurationUtils.getSharedSecret(
+                        tksURI.getHost(),
+                        tksURI.getPort(),
+                        Boolean.getBoolean(data.getImportSharedSecret()));
+
             } catch (URISyntaxException e) {
                 throw new BadRequestException("Invalid URI for CA, TKS or KRA");
             } catch (Exception e) {
@@ -1146,6 +1153,12 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             }
 
             // TODO check connection with authdb
+
+            if (data.getImportSharedSecret().equalsIgnoreCase("true")) {
+                data.setImportSharedSecret("true");
+            } else {
+                data.setImportSharedSecret("false");
+            }
         }
     }
 }
diff --git a/base/common/src/com/netscape/cms/servlet/tks/TPSConnectorService.java b/base/common/src/com/netscape/cms/servlet/tks/TPSConnectorService.java
new file mode 100644
index 0000000000000000000000000000000000000000..97478d6994fd796a50b1661c4d8ba87cf73e092c
--- /dev/null
+++ b/base/common/src/com/netscape/cms/servlet/tks/TPSConnectorService.java
@@ -0,0 +1,376 @@
+package com.netscape.cms.servlet.tks;
+
+import java.io.IOException;
+import java.net.URI;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.commons.lang.StringUtils;
+import org.jboss.resteasy.plugins.providers.atom.Link;
+import org.mozilla.jss.CryptoManager.NotInitializedException;
+import org.mozilla.jss.crypto.InvalidKeyFormatException;
+import org.mozilla.jss.crypto.TokenException;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.BadRequestException;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.base.UnauthorizedException;
+import com.netscape.certsrv.key.KeyData;
+import com.netscape.certsrv.system.TPSConnectorCollection;
+import com.netscape.certsrv.system.TPSConnectorData;
+import com.netscape.certsrv.system.TPSConnectorResource;
+import com.netscape.certsrv.tps.cert.TPSCertResource;
+import com.netscape.certsrv.usrgrp.IUGSubsystem;
+import com.netscape.certsrv.usrgrp.IUser;
+import com.netscape.cmscore.realm.PKIPrincipal;
+import com.netscape.cmsutil.crypto.CryptoUtil;
+import com.netscape.cmsutil.util.Utils;
+
+public class TPSConnectorService implements TPSConnectorResource {
+
+    IConfigStore cs = CMS.getConfigStore();
+
+    @Context
+    private UriInfo uriInfo;
+
+    @Context
+    private HttpServletRequest servletRequest;
+
+    public IUGSubsystem userGroupManager = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
+
+    @Override
+    public TPSConnectorCollection listConnectors() {
+        try {
+            String tpsList = cs.getString("tps.list", "");
+            if (tpsList.isEmpty()) {
+                return null;
+            }
+
+            TPSConnectorCollection ret = new TPSConnectorCollection();
+            for (String tpsID : tpsList.split(",")) {
+                ret.addEntry(createTPSSystemClientData(tpsID));
+            }
+            return ret;
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException("Unable to get TPS connection data" + e);
+        }
+    }
+
+    private TPSConnectorData createTPSSystemClientData(String tpsID) throws EBaseException {
+        TPSConnectorData data = new TPSConnectorData();
+        data.setID(tpsID);
+        data.setHost(cs.getString("tps." + tpsID + ".host", ""));
+        data.setPort(cs.getString("tps." + tpsID + ".port", ""));
+        data.setUserID(cs.getString("tps." + tpsID + ".userid", ""));
+        data.setNickname(cs.getString("tps." + tpsID + ".nickname", ""));
+        URI uri = uriInfo.getBaseUriBuilder().path(TPSCertResource.class).path("{id}").build(tpsID);
+        data.setLink(new Link("self", uri));
+        return data;
+    }
+
+    @Override
+    public TPSConnectorData getConnector(String id) {
+        try {
+            String tpsList = cs.getString("tps.list", "");
+            if (tpsList.isEmpty()) {
+                return null;
+            }
+
+            for (String tpsID : tpsList.split(",")) {
+                if (tpsID.equals(id))
+                    return createTPSSystemClientData(tpsID);
+            }
+            return null;
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException("Unable to get TPS connection data" + e);
+        }
+    }
+
+    @Override
+    public TPSConnectorData getConnector(String host, String port) {
+        try {
+            String tpsList = cs.getString("tps.list", "");
+            if (tpsList.isEmpty()) {
+                return null;
+            }
+
+            for (String tpsID : tpsList.split(",")) {
+                TPSConnectorData data = createTPSSystemClientData(tpsID);
+                if (data.getHost().equals(host) && data.getPort().equals(port))
+                    return data;
+            }
+            return null;
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException("Unable to get TPS connection data" + e);
+        }
+    }
+
+    @Override
+    public TPSConnectorData createConnector(String tpsHost, String tpsPort) {
+        TPSConnectorData newData = new TPSConnectorData();
+        newData.setHost(tpsHost);
+        newData.setPort(tpsPort);
+        newData.setUserID("TPS-" + tpsHost + "-" + tpsPort);
+        try {
+            int index = 0;
+            boolean indexFound = false;
+            String tpsList = cs.getString("tps.list", "");
+            if (!tpsList.isEmpty()) {
+                List<String> sorted = new ArrayList<String>(Arrays.asList(tpsList.split(",")));
+                Collections.sort(sorted);
+                for (String tpsID : sorted) {
+                    TPSConnectorData data = createTPSSystemClientData(tpsID);
+                    if (data.equals(newData)) {
+                        throw new BadRequestException("TPS connection already exists at " + data.getLink());
+                    }
+                    if (!indexFound && tpsID.equals(index)) {
+                        index++;
+                    } else {
+                        indexFound = true;
+                    }
+                }
+            }
+            String newID = Integer.toString(index);
+            newData.setID(newID);
+            URI uri = uriInfo.getBaseUriBuilder().path(TPSCertResource.class).path("{id}").build(newID);
+            newData.setLink(new Link("self", uri));
+            saveClientData(newData);
+
+            cs.putString("tps.list", tpsList.isEmpty() ? Integer.toString(index) :
+                    tpsList + "," + index);
+            cs.commit(false);
+
+            return newData;
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException("Unable to create  new TPS connection data" + e);
+        }
+    }
+
+    private void saveClientData(TPSConnectorData newData) throws EBaseException {
+        String id = newData.getID();
+        if ((id == null) || (id.isEmpty())) {
+            CMS.debug("saveClientData: Attempt to save tps connection with null or empty id");
+            return;
+            // throw exception here?
+        }
+        String prefix = "tps." + id + ".";
+
+        if (newData.getHost() != null)
+            cs.putString(prefix + "host", newData.getHost());
+        if (newData.getPort() != null)
+            cs.putString(prefix + "port", newData.getPort());
+        if (newData.getUserID() != null)
+            cs.putString(prefix + "userid", newData.getUserID());
+        if (newData.getNickname() != null)
+            cs.putString(prefix + "nickname", newData.getNickname());
+
+        cs.commit(false);
+    }
+
+    @Override
+    public void deleteConnector(String id) {
+        try {
+            if ((id == null) || id.isEmpty())
+                throw new BadRequestException("Attempt to delete TPS connection with null or empty id");
+
+            if (getConnector(id) == null) {
+                return;
+                // return 404 here?
+            }
+
+            deleteSharedSecret(id);
+
+            String prefix = "tps." + id;
+            cs.removeSubStore(prefix);
+
+            String tpsList = cs.getString("tps.list", "");
+            if (tpsList.isEmpty()) {
+                return;
+            }
+
+            List<String> newList = new ArrayList<String>();
+            for (String tpsID : tpsList.split(",")) {
+                if (!tpsID.equals(id)) {
+                    newList.add(tpsID);
+                }
+            }
+            cs.putString("tps.list", StringUtils.join(newList, ","));
+            cs.commit(false);
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException("Failed to delete TPS connection" + e);
+        }
+    }
+
+    @Override
+    public KeyData createSharedSecret(String id) {
+        try {
+            if (getConnector(id) == null) {
+                throw new BadRequestException("TPS Connection does not exist");
+            }
+
+            // get and validate user
+            String userid = validateUser(id);
+
+            // get user cert
+            IUser user = userGroupManager.getUser(userid);
+            X509Certificate[] certs = user.getX509Certificates();
+
+            String nickname = userid + " sharedSecret";
+            if (CryptoUtil.sharedSecretExists(nickname)) {
+                throw new BadRequestException("Shared secret already exists");
+            }
+
+            CryptoUtil.createSharedSecret(nickname);
+
+            cs.putString("tps." + id + ".nickname", nickname);
+            cs.commit(false);
+
+            byte[] wrappedKey = CryptoUtil.exportSharedSecret(nickname, certs[0]);
+            KeyData keyData = new KeyData();
+            keyData.setWrappedPrivateData(Utils.base64encode(wrappedKey));
+            return keyData;
+
+        } catch (InvalidKeyException | IllegalStateException | NoSuchAlgorithmException
+                | InvalidAlgorithmParameterException | EBaseException
+                | NotInitializedException | TokenException | IOException | InvalidKeyFormatException e) {
+            e.printStackTrace();
+            CMS.debug("Error in generating and exporting shared secret: " + e);
+            throw new PKIException("Error in generating and exporting shared secret: " + e);
+        }
+    }
+
+    private String validateUser(String id) throws EBaseException {
+        String userid = cs.getString("tps." + id + ".userid", "");
+        if (userid.isEmpty()) {
+            throw new PKIException("Bad TPS connection configuration: userid not defined");
+        }
+
+        PKIPrincipal principal = (PKIPrincipal) servletRequest.getUserPrincipal();
+        if (principal == null) {
+            throw new UnauthorizedException("User credentials not provided");
+        }
+
+        String uid = principal.getName();
+        if (!uid.equals(userid)) {
+            throw new UnauthorizedException("TPS Connection belongs to another user");
+        }
+        return userid;
+    }
+
+    @Override
+    public KeyData replaceSharedSecret(String id) {
+        try {
+            if (getConnector(id) == null) {
+                throw new BadRequestException("TPS Connection does not exist");
+            }
+
+            // get and validate user
+            String userid = validateUser(id);
+
+            // get user cert
+            IUser user = userGroupManager.getUser(userid);
+            X509Certificate[] certs = user.getX509Certificates();
+
+            String nickname = userid + " sharedSecret";
+            if (!CryptoUtil.sharedSecretExists(nickname)) {
+                throw new BadRequestException("Cannot replace. Shared secret does not exist");
+            }
+
+            CryptoUtil.deleteSharedSecret(nickname);
+            CryptoUtil.createSharedSecret(nickname);
+            byte[] wrappedKey = CryptoUtil.exportSharedSecret(nickname, certs[0]);
+            KeyData keyData = new KeyData();
+            keyData.setWrappedPrivateData(Utils.base64encode(wrappedKey));
+            return keyData;
+        } catch (InvalidKeyException | IllegalStateException | NoSuchAlgorithmException
+                | InvalidAlgorithmParameterException | EBaseException
+                | NotInitializedException | TokenException | IOException | InvalidKeyFormatException e) {
+            e.printStackTrace();
+            CMS.debug("Error in replacing shared secret: " + e);
+            throw new PKIException("Error in replacing shared secret: " + e);
+        }
+    }
+
+    @Override
+    public void deleteSharedSecret(String id) {
+        try {
+            if (getConnector(id) == null) {
+                return;
+            }
+
+            // get and validate user
+            String userid = validateUser(id);
+
+            String nickname = userid + " sharedSecret";
+            if (!CryptoUtil.sharedSecretExists(nickname)) {
+                return;
+            }
+            CryptoUtil.deleteSharedSecret(nickname);
+
+            cs.putString("tps." + id + ".nickname", "");
+            cs.commit(false);
+        } catch (InvalidKeyException | IllegalStateException | EBaseException
+                | NotInitializedException | TokenException e) {
+            e.printStackTrace();
+            CMS.debug("Error in deleting shared secret: " + e);
+            throw new PKIException("Error in deleting shared secret: " + e);
+        }
+    }
+
+    @Override
+    public KeyData getSharedSecret(String id) {
+        try {
+            if (getConnector(id) == null) {
+                throw new BadRequestException("TPS Connection does not exist");
+            }
+
+            // get and validate user
+            String userid = validateUser(id);
+
+            // get user cert
+            IUser user = userGroupManager.getUser(userid);
+            X509Certificate[] certs = user.getX509Certificates();
+
+            String nickname = userid + " sharedSecret";
+            if (!CryptoUtil.sharedSecretExists(nickname)) {
+                return null;
+            }
+            byte[] wrappedKey = CryptoUtil.exportSharedSecret(nickname, certs[0]);
+            KeyData keyData = new KeyData();
+            keyData.setWrappedPrivateData(Utils.base64encode(wrappedKey));
+            return keyData;
+        } catch (InvalidKeyException | IllegalStateException | NoSuchAlgorithmException
+                | InvalidAlgorithmParameterException | EBaseException
+                | NotInitializedException | TokenException | IOException | InvalidKeyFormatException e) {
+            e.printStackTrace();
+            CMS.debug("Error in obtaining shared secret: " + e);
+            throw new PKIException("Error in obtaining shared secret: " + e);
+        }
+    }
+
+    @Override
+    public void deleteConnector(String host, String port) {
+        TPSConnectorData data = getConnector(host, port);
+        if (data == null) {
+            return;
+        }
+        deleteConnector(data.getID());
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java
index df7050189b541574954f0e797cdd451db956fecc..b8c90ed4712a092e661a35fe5a5c2c37e0c1a216 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/TKSCLI.java
@@ -21,6 +21,7 @@ package com.netscape.cmstools.cli;
 import com.netscape.certsrv.client.Client;
 import com.netscape.certsrv.tks.TKSClient;
 import com.netscape.cmstools.group.GroupCLI;
+import com.netscape.cmstools.system.TPSConnectorCLI;
 import com.netscape.cmstools.user.UserCLI;
 
 /**
@@ -34,6 +35,7 @@ public class TKSCLI extends SubsystemCLI {
         super("tks", "TKS management commands", parent);
 
         addModule(new GroupCLI(this));
+        addModule(new TPSConnectorCLI(this));
         addModule(new UserCLI(this));
     }
 
diff --git a/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorAddCLI.java b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorAddCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9e1df985d37af1df74d417e7dbcf3404620ee48
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorAddCLI.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) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cmstools.system;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Ade Lee
+ */
+public class TPSConnectorAddCLI extends CLI {
+    public TPSConnectorCLI tpsConnectorCLI;
+
+    public TPSConnectorAddCLI(TPSConnectorCLI tpsConnectorCLI) {
+        super("add", "Add TPS Connector to TKS", tpsConnectorCLI);
+        this.tpsConnectorCLI = tpsConnectorCLI;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " <TPS Host> <TPS Port>", options);
+    }
+
+    public void execute(String[] args) throws Exception {
+        if (args.length != 2) {
+            printHelp();
+            System.exit(1);
+        }
+
+        String tpsHost = args[0];
+        String tpsPort = args[1];
+
+        tpsConnectorCLI.tpsConnectorClient.createConnector(tpsHost, tpsPort);
+
+        MainCLI.printMessage("Added TPS connector \""+tpsHost + ":" + tpsPort +"\"");
+    }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorCLI.java b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a87c34287f9fd9b32e26f0cff7c774ffd601b49
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorCLI.java
@@ -0,0 +1,94 @@
+// --- 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 com.netscape.cmstools.system;
+
+import java.util.Arrays;
+
+import org.jboss.resteasy.plugins.providers.atom.Link;
+
+import com.netscape.certsrv.system.TPSConnectorClient;
+import com.netscape.certsrv.system.TPSConnectorData;
+import com.netscape.cmstools.cli.CLI;
+
+/**
+ * @author Ade Lee
+ */
+public class TPSConnectorCLI extends CLI {
+
+    public TPSConnectorClient tpsConnectorClient;
+
+    public TPSConnectorCLI(CLI parent) {
+        super("tpsconnector", "TPS Connector management commands", parent);
+
+        addModule(new TPSConnectorAddCLI(this));
+        addModule(new TPSConnectorFindCLI(this));
+        addModule(new TPSConnectorRemoveCLI(this));
+    }
+
+    public String getFullName() {
+        return parent.getFullName() + "-" + name;
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        client = parent.getClient();
+        tpsConnectorClient = (TPSConnectorClient)parent.getClient("tpsconnector");
+
+        if (tpsConnectorClient == null) {
+            // if parent doesn't have user client then create a new one
+            tpsConnectorClient = new TPSConnectorClient(client);
+        }
+
+        if (args.length == 0) {
+            printHelp();
+            System.exit(1);
+        }
+
+        String command = args[0];
+        String[] commandArgs = Arrays.copyOfRange(args, 1, args.length);
+
+        if (command == null) {
+            printHelp();
+            System.exit(1);
+        }
+
+        CLI module = getModule(command);
+        if (module != null) {
+            module.execute(commandArgs);
+
+        } else {
+            System.err.println("Error: Invalid command \""+command+"\"");
+            printHelp();
+            System.exit(1);
+        }
+    }
+
+    public static void printConnectorInfo(TPSConnectorData data) {
+        System.out.println("  Connector ID: " + data.getID());
+        if (data.getHost() != null) System.out.println("  Host: " + data.getHost());
+        if (data.getPort() != null) System.out.println("  Port: " + data.getPort());
+        if (data.getUserID() != null) System.out.println("  User ID: " + data.getUserID());
+        if (data.getNickname() != null) System.out.println("  Nickname: " + data.getNickname());
+
+        Link link = data.getLink();
+        if (verbose && link != null) {
+            System.out.println("  Link: " + link.getHref());
+        }
+    }
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorFindCLI.java b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorFindCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a42b7d3ff8de23f8f341d1cbcf2654dec83406e
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorFindCLI.java
@@ -0,0 +1,88 @@
+// --- 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 com.netscape.cmstools.system;
+
+import java.util.Collection;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+
+import com.netscape.certsrv.system.TPSConnectorCollection;
+import com.netscape.certsrv.system.TPSConnectorData;
+import com.netscape.cmstools.cli.CLI;
+
+/**
+ * @author Ade Lee
+ */
+public class TPSConnectorFindCLI extends CLI {
+    public TPSConnectorCLI tpsConnectorCLI;
+
+    public TPSConnectorFindCLI(TPSConnectorCLI tpsConnectorCLI) {
+        super("find", "Find TPS connector details on TKS", tpsConnectorCLI);
+        this.tpsConnectorCLI = tpsConnectorCLI;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " [OPTIONS...]", options);
+    }
+
+    public void execute(String[] args) throws Exception {
+        Option option = new Option(null, "host", true, "TPS host");
+        option.setArgName("host");
+        options.addOption(option);
+
+        option = new Option(null, "port", true, "TPS port");
+        option.setArgName("port");
+        options.addOption(option);
+
+        CommandLine cmd = null;
+
+        try {
+            cmd = parser.parse(options, args);
+
+        } catch (Exception e) {
+            System.err.println("Error: " + e.getMessage());
+            printHelp();
+            System.exit(1);
+        }
+
+        String tpsHost = cmd.getOptionValue("host");
+        String tpsPort = cmd.getOptionValue("port");
+
+        if (tpsHost != null) {
+            if (tpsPort == null) tpsPort = "443";
+            TPSConnectorData data = tpsConnectorCLI.tpsConnectorClient.getConnector(
+                    tpsHost, tpsPort);
+            TPSConnectorCLI.printConnectorInfo(data);
+        } else {
+            TPSConnectorCollection result = tpsConnectorCLI.tpsConnectorClient.listConnectors();
+            Collection<TPSConnectorData> conns = result.getEntries();
+
+            boolean first = true;
+            for (TPSConnectorData data: conns) {
+                if (first) {
+                    first = false;
+                } else {
+                    System.out.println();
+                }
+
+                TPSConnectorCLI.printConnectorInfo(data);
+            }
+        }
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorRemoveCLI.java b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorRemoveCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..f721fe28f0a3332d80a773ee24f1707c0c0da8b1
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/TPSConnectorRemoveCLI.java
@@ -0,0 +1,53 @@
+// --- 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 com.netscape.cmstools.system;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Ade Lee
+ */
+public class TPSConnectorRemoveCLI extends CLI {
+    public TPSConnectorCLI tpsConnectorCLI;
+
+    public TPSConnectorRemoveCLI(TPSConnectorCLI tpsConnectorCLI) {
+        super("del", "Remove TPS connector from TKS", tpsConnectorCLI);
+        this.tpsConnectorCLI = tpsConnectorCLI;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " <TPS Host> <TPS Port>", options);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        if (args.length != 2) {
+            printHelp();
+            System.exit(1);
+        }
+
+        String tpsHost = args[0];
+        String tpsPort = args[1];
+
+        tpsConnectorCLI.tpsConnectorClient.deleteConnector(tpsHost, tpsPort);
+
+        MainCLI.printMessage("Removed TPS connector \""+tpsHost + ":" + tpsPort +"\"");
+    }
+
+}
diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index d8ca7fe1f3eaa1b70637b09b753253117c97cb9c..88f9f780c2972a6159d023f16fa3ad3530af332d 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -551,6 +551,7 @@ pki_ca_uri=https://%(pki_hostname)s:%(pki_https_port)s
 pki_kra_uri=https://%(pki_hostname)s:%(pki_https_port)s
 pki_tks_uri=https://%(pki_hostname)s:%(pki_https_port)s
 pki_enable_server_side_keygen=False
+pki_import_shared_secret=False
 
 # Paths
 # These are used in the processing of pkispawn and are not supposed
diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 9257cbfb89c873f0c8c845fcccc0fa2064e74c32..ce800471b5957e64a8c92e106c334d3d9b68284f 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -2721,6 +2721,129 @@ class KRAConnector:
         # and this will raise an exception
         subprocess.check_output(command,stderr=subprocess.STDOUT)
 
+class TPSConnector:
+    """PKI Deployment TPS Connector Class"""
+
+    def __init__(self, deployer):
+        self.master_dict = deployer.master_dict
+        self.password = deployer.password
+
+    def deregister(self, critical_failure=False):
+        try:
+            # this is applicable to TPSs only
+            if self.master_dict['pki_subsystem_type'] != "tps":
+                return
+
+            config.pki_log.info(
+                log.PKIHELPER_TPSCONNECTOR_UPDATE_CONTACT,
+                extra=config.PKI_INDENTATION_LEVEL_2)
+
+            cs_cfg = PKIConfigParser.read_simple_configuration_file(
+                         self.master_dict['pki_target_cs_cfg'])
+            tpshost = cs_cfg.get('service.machineName')
+            tpsport = cs_cfg.get('pkicreate.secure_port')
+            tkshostport = cs_cfg.get('conn.tks1.hostport')
+            if tkshostport is None:
+                config.pki_log.warning(
+                    log.PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.error(
+                    log.PKIHELPER_UNDEFINED_TKS_HOST_PORT,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    raise Exception(log.PKIHELPER_UNDEFINED_TKS_HOST_PORT)
+                else:
+                    return
+
+            #retrieve tks host and port
+            if ':' in tkshostport:
+                tkshost = tkshostport.split(':')[0]
+                tksport = tkshostport.split(':')[1]
+            else:
+                tkshost = tkshostport
+                tksport = '443'
+
+            # retrieve subsystem nickname
+            subsystemnick = cs_cfg.get('tps.cert.subsystem.nickname')
+            if subsystemnick is None:
+                config.pki_log.warning(
+                    log.PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.error(
+                    log.PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    raise Exception(log.PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME)
+                else:
+                    return
+
+            # retrieve name of token based upon type (hardware/software)
+            if ':' in subsystemnick:
+                token_name = subsystemnick.split(':')[0]
+            else:
+                token_name = "internal"
+
+            token_pwd = self.password.get_password(
+                            self.master_dict['pki_shared_password_conf'],
+                            token_name,
+                            critical_failure)
+
+            if token_pwd is None or token_pwd == '':
+                config.pki_log.warning(
+                    log.PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.error(
+                    log.PKIHELPER_UNDEFINED_TOKEN_PASSWD_1,
+                    token_name,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    raise Exception(log.PKIHELPER_UNDEFINED_TOKEN_PASSWD_1 % token_name)
+                else:
+                    return
+
+            self.execute_using_pki(tkshost, tksport, subsystemnick,
+                                 token_pwd, tpshost, tpsport)
+
+        except subprocess.CalledProcessError as exc:
+            config.pki_log.warning(
+                log.PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE_2,
+                str(tkshost),
+                str(tksport),
+                extra=config.PKI_INDENTATION_LEVEL_2)
+            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                raise
+        return
+
+    def execute_using_pki(self, tkshost, tksport, subsystemnick,
+      token_pwd, tpshost, tpsport, critical_failure=False):
+        command = ["/bin/pki",
+                   "-p", str(tksport),
+                   "-h", tkshost,
+                   "-n", subsystemnick,
+                   "-P", "https",
+                   "-d", self.master_dict['pki_database_path'],
+                   "-c", token_pwd,
+                   "-t", "tks",
+                   "tks-tpsconnector-del", tpshost, str(tpsport)]
+
+        output = subprocess.check_output(command,
+                                         stderr=subprocess.STDOUT,
+                                         shell=False)
+
+        error = re.findall("ClientResponseFailure:(.*?)", output)
+        if error:
+            config.pki_log.warning(
+                log.PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE_2,
+                str(tpshost),
+                str(tpsport),
+                extra=config.PKI_INDENTATION_LEVEL_2)
+            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, output,
+                extra=config.PKI_INDENTATION_LEVEL_2)
+        if critical_failure == True:
+            raise Exception(log.PKI_SUBPROCESS_ERROR_1 % output)
+
 class SecurityDomain:
     """PKI Deployment Security Domain Class"""
 
@@ -3498,6 +3621,7 @@ class ConfigClient:
         data.authdbPort = self.master_dict['pki_authdb_port']
         data.authdbBaseDN = self.master_dict['pki_authdb_basedn']
         data.authdbSecureConn = self.master_dict['pki_authdb_secure_conn']
+        data.importSharedSecret = self.master_dict['pki_import_shared_secret']
 
     def create_system_cert(self, tag):
         cert = pki.system.SystemCertData()
@@ -3549,6 +3673,7 @@ class PKIDeployer:
         self.kra_connector = KRAConnector(self)
         self.security_domain = SecurityDomain(self)
         self.systemd = Systemd(self)
+        self.tps_connector = TPSConnector(self)
         self.config_client = ConfigClient(self)
 
 
diff --git a/base/server/python/pki/server/deployment/pkimessages.py b/base/server/python/pki/server/deployment/pkimessages.py
index a3be42e9730066ba699f1ee4d82a3c6a98fe1e94..339ee149ea9852469e933c9cdcb6be7b3b17fc19 100644
--- a/base/server/python/pki/server/deployment/pkimessages.py
+++ b/base/server/python/pki/server/deployment/pkimessages.py
@@ -272,6 +272,10 @@ PKIHELPER_TOMCAT_INSTANCE_SUBSYSTEMS_2 = "instance '%s' contains '%d' "\
 PKIHELPER_TOMCAT_INSTANCES_2 = "PKI Tomcat registry '%s' contains '%d' "\
                                "Tomcat PKI instances"
 PKIHELPER_TOUCH_1 = "touch %s"
+PKIHELPER_TPSCONNECTOR_UPDATE_CONTACT = \
+    "contacting the TKS to update the TPS connector"
+PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE = "Failed to update TPS connector on TKS"
+PKIHELPER_TPSCONNECTOR_UPDATE_FAILURE_2 = "Failed to update TPS connector for %s:%s"
 PKIHELPER_UID_2 = "UID of '%s' is %s"
 PKIHELPER_UNDEFINED_CA_HOST_PORT = "CA Host or Port is undefined"
 PKIHELPER_UNDEFINED_CLIENT_DATABASE_PASSWORD_2 = \
@@ -280,6 +284,7 @@ PKIHELPER_UNDEFINED_CLIENT_DATABASE_PASSWORD_2 = \
 PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2 = \
     "A value for '%s' MUST be defined in '%s'"
 PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME = "subsystem nickname not defined"
+PKIHELPER_UNDEFINED_TKS_HOST_PORT = "TKS Host or Port is undefined"
 PKIHELPER_UNDEFINED_TOKEN_PASSWD_1 = "Password for token '%s' not defined"
 PKIHELPER_USER_1 = "retrieving UID for '%s' . . ."
 PKIHELPER_USER_ADD_2 = "adding UID '%s' for user '%s' . . ."
diff --git a/base/server/python/pki/server/deployment/scriptlets/initialization.py b/base/server/python/pki/server/deployment/scriptlets/initialization.py
index 54349fc01efbe6622f6ceed053bffa6f664dd684..ecfb4d1956de53673431e7e937cc7a0c6daada9f 100644
--- a/base/server/python/pki/server/deployment/scriptlets/initialization.py
+++ b/base/server/python/pki/server/deployment/scriptlets/initialization.py
@@ -99,6 +99,9 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # remove kra connector from CA if this is a KRA
         deployer.kra_connector.deregister()
 
+        # remove tps connector from TKS if this is a TPS
+        deployer.tps_connector.deregister()
+
         # de-register instance from its Security Domain
         #
         #     NOTE:  Since the security domain of an instance must be up
diff --git a/base/tks/src/com/netscape/tks/TKSApplication.java b/base/tks/src/com/netscape/tks/TKSApplication.java
index b2d43ae87b7c47c519f777d93eb741c6257afa76..1ec0576e47f3a0addbda4085e57ac9322a5ba78c 100644
--- a/base/tks/src/com/netscape/tks/TKSApplication.java
+++ b/base/tks/src/com/netscape/tks/TKSApplication.java
@@ -16,6 +16,7 @@ import com.netscape.cms.servlet.admin.UserCertService;
 import com.netscape.cms.servlet.admin.UserMembershipService;
 import com.netscape.cms.servlet.admin.UserService;
 import com.netscape.cms.servlet.csadmin.SystemConfigService;
+import com.netscape.cms.servlet.tks.TPSConnectorService;
 
 public class TKSApplication extends Application {
 
@@ -40,6 +41,9 @@ public class TKSApplication extends Application {
         // system certs
         classes.add(SystemCertService.class);
 
+        // tps connections and shared secrets
+        classes.add(TPSConnectorService.class);
+
         // exception mapper
         classes.add(PKIException.Mapper.class);
 
diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
index 5957f046d3ae0718d18df5bd7573fe5ac1e91da2..c8941777d4f3b10d1eae5048ec18b7bb3b693974 100644
--- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
+++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
@@ -42,6 +42,8 @@ import java.util.HashMap;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+import javax.crypto.SecretKey;
+
 import netscape.security.pkcs.PKCS10;
 import netscape.security.pkcs.PKCS7;
 import netscape.security.util.BigInt;
@@ -68,6 +70,7 @@ import netscape.security.x509.X509Key;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
 import org.mozilla.jss.NoSuchTokenException;
+import org.mozilla.jss.SecretDecoderRing.KeyManager;
 import org.mozilla.jss.asn1.ASN1Util;
 import org.mozilla.jss.asn1.BIT_STRING;
 import org.mozilla.jss.asn1.InvalidBERException;
@@ -95,12 +98,14 @@ import org.mozilla.jss.crypto.NoSuchItemOnTokenException;
 import org.mozilla.jss.crypto.ObjectNotFoundException;
 import org.mozilla.jss.crypto.PBEAlgorithm;
 import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.SecretKeyFacade;
 import org.mozilla.jss.crypto.Signature;
 import org.mozilla.jss.crypto.SignatureAlgorithm;
 import org.mozilla.jss.crypto.SymmetricKey;
 import org.mozilla.jss.crypto.TokenException;
 import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.pkcs11.PK11ECPublicKey;
+import org.mozilla.jss.pkcs11.PK11PubKey;
 import org.mozilla.jss.pkcs12.PasswordConverter;
 import org.mozilla.jss.pkcs7.EncryptedContentInfo;
 import org.mozilla.jss.pkix.crmf.CertReqMsg;
@@ -1639,6 +1644,78 @@ public class CryptoUtil {
         return encoded;
     }
 
+    public static boolean sharedSecretExists(String nickname) throws NotInitializedException, TokenException {
+        CryptoManager cm = CryptoManager.getInstance();
+        CryptoToken token = cm.getInternalKeyStorageToken();
+        KeyManager km = new KeyManager(token);
+        return km.uniqueNamedKeyExists(nickname);
+    }
+
+    public static void createSharedSecret(String nickname) throws NotInitializedException, TokenException {
+        CryptoManager cm = CryptoManager.getInstance();
+        CryptoToken token = cm.getInternalKeyStorageToken();
+        KeyManager km = new KeyManager(token);
+        km.generateUniqueNamedKey(nickname);
+    }
+
+    public static void deleteSharedSecret(String nickname) throws NotInitializedException, TokenException,
+            InvalidKeyException {
+        CryptoManager cm = CryptoManager.getInstance();
+        CryptoToken token = cm.getInternalKeyStorageToken();
+        KeyManager km = new KeyManager(token);
+        km.deleteUniqueNamedKey(nickname);
+    }
+
+    public static byte[] exportSharedSecret(String nickname, java.security.cert.X509Certificate wrappingCert)
+            throws NotInitializedException, TokenException, IOException, NoSuchAlgorithmException, InvalidKeyException,
+            InvalidAlgorithmParameterException, InvalidKeyFormatException {
+        CryptoManager cm = CryptoManager.getInstance();
+        CryptoToken token = cm.getInternalKeyStorageToken();
+        KeyManager km = new KeyManager(token);
+        if (!km.uniqueNamedKeyExists(nickname)) {
+            throw new IOException("Shared secret " + nickname + " does not exist");
+        }
+        SecretKey skey = km.lookupUniqueNamedKey(EncryptionAlgorithm.DES3_ECB, nickname);
+
+        KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.RSA);
+        PublicKey pub = wrappingCert.getPublicKey();
+        PK11PubKey pubK = PK11PubKey.fromSPKI(pub.getEncoded());
+        keyWrap.initWrap(pubK, null);
+        byte[] wrappedKey = keyWrap.wrap(((SecretKeyFacade) skey).key);
+        return wrappedKey;
+    }
+
+    /*
+    public static void importSharedSecret(KeyData data) throws EBaseException, NotInitializedException, TokenException,
+            NoSuchAlgorithmException, ObjectNotFoundException, InvalidKeyException, InvalidAlgorithmParameterException,
+            IOException {
+        byte[] wrappedKey = Utils.base64decode(data.getWrappedPrivateData());
+
+        IConfigStore cs = CMS.getConfigStore();
+        String subsystemNick = cs.getString("tps.cert.subsystem.nickname");
+        String keyNick = cs.getString("conn.tks1.tksSharedSymKeyName", "sharedSecret");
+
+        CryptoManager cm = CryptoManager.getInstance();
+        CryptoToken token = cm.getInternalKeyStorageToken();
+        KeyManager km = new KeyManager(token);
+
+        if (km.uniqueNamedKeyExists(keyNick)) {
+            throw new IOException("Shared secret " + keyNick + " already exists");
+        }
+
+        KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.RSA);
+        X509Certificate cert = cm.findCertByNickname(subsystemNick);
+        PrivateKey subsystemPrivateKey = cm.findPrivKeyByCert(cert);
+        keyWrap.initUnwrap(subsystemPrivateKey, null);
+
+        @SuppressWarnings("unused")
+        SymmetricKey unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES,
+                SymmetricKey.Usage.DECRYPT, 0);
+
+        // TODO - I have a key - now what to do with it?
+        // need to somehow import/label the symkey
+    }*/
+
     public static String[] getECcurves() {
         return ecCurves;
     }
@@ -1667,7 +1744,6 @@ public class CryptoUtil {
     }
 }
 
-
 // START ENABLE_ECC
 // This following can be removed when JSS with ECC capability
 // is integrated.
-- 
1.8.3.1

