>From b6e61d974850f4ff2a51c361f3afc2446ead71b1 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Thu, 20 Dec 2012 17:38:13 -0500
Subject: [PATCH] Resolved Trac Ticket 367 - pkidestroy does not remove
 connector

* Added RESTful servlet to add/remove a KRA connector from the CA.
* Modified ACL to allow KRA subsystem user to remove connector.
* Modified connector code to allow the connector to be replaced without a server restart.
* Added functionality to pki CLI to add/remove connector
* Added code to pkidestroy to remove the connector (using both pki CLI and sslget)
  When the issues with pki connection are resolved, we will use that method instead.
* Modified sslget to accept HTTP return codes != 200.  In this case, we were returning
  204 - which is perfectly legitimate.
---
 base/ca/shared/conf/acl.ldif                       |   2 +-
 base/ca/shared/webapps/ca/WEB-INF/auth.properties  |   1 +
 .../ca/CertificateAuthorityApplication.java        |   4 +
 .../com/netscape/certsrv/connector/IConnector.java |   5 +
 .../com/netscape/certsrv/connector/IResender.java  |   3 +
 .../certsrv/system/KRAConnectorClient.java         |  54 +++++
 .../netscape/certsrv/system/KRAConnectorInfo.java  | 184 +++++++++++++++++
 .../certsrv/system/KRAConnectorResource.java       |  55 +++++
 .../cms/servlet/admin/KRAConnectorProcessor.java   | 223 +++++++++++++++++++++
 .../cms/servlet/admin/KRAConnectorService.java     |  61 ++++++
 .../cms/servlet/csadmin/UpdateConnector.java       |  89 ++++----
 .../netscape/cmscore/connector/HttpConnector.java  |  11 +-
 .../netscape/cmscore/connector/LocalConnector.java |   3 +
 .../com/netscape/cmscore/connector/Resender.java   |  66 +++---
 base/deploy/src/scriptlets/initialization.py       |   4 +
 base/deploy/src/scriptlets/pkihelper.py            | 178 ++++++++++++++--
 base/deploy/src/scriptlets/pkimessages.py          |   6 +
 .../src/com/netscape/cmstools/cli/MainCLI.java     |   2 +
 .../cmstools/system/KRAConnectorAddCLI.java        |  77 +++++++
 .../netscape/cmstools/system/KRAConnectorCLI.java  |  92 +++++++++
 .../cmstools/system/KRAConnectorRemoveCLI.java     |  52 +++++
 base/native-tools/src/sslget/sslget.c              |   6 +-
 22 files changed, 1070 insertions(+), 108 deletions(-)
 create mode 100644 base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java
 create mode 100644 base/common/src/com/netscape/certsrv/system/KRAConnectorInfo.java
 create mode 100644 base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java
 create mode 100644 base/common/src/com/netscape/cms/servlet/admin/KRAConnectorProcessor.java
 create mode 100644 base/common/src/com/netscape/cms/servlet/admin/KRAConnectorService.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/KRAConnectorAddCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/KRAConnectorCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/system/KRAConnectorRemoveCLI.java

diff --git a/base/ca/shared/conf/acl.ldif b/base/ca/shared/conf/acl.ldif
index c7d71f9e604fb3dd9b7b0091a9c515ccd9602b8f..7321792167315ccc79fe5e56b9c3afb32c7f480e 100644
--- a/base/ca/shared/conf/acl.ldif
+++ b/base/ca/shared/conf/acl.ldif
@@ -47,7 +47,7 @@ resourceACLS: certServer.ca.connector:submit:allow (submit) group="Trusted Manag
 resourceACLS: certServer.ca.clone:submit:allow (submit) group="Certificate Manager Agents":Certificate Manager Agents are allowed to submit request to the master CA
 resourceACLS: certServer.ca.systemstatus:read:allow (read) group="Certificate Manager Agents":Certificate Manager agents may view statistics
 resourceACLS: certServer.ca.group:read,modify:allow (modify,read) group="Administrators":Only administrators are allowed to read and modify users and groups
-resourceACLS: certServer.ca.connectorInfo:read,modify:allow (modify,read) group="Enterprise KRA Administrators":Only Enterprise Administrators are allowed to update the connector information
+resourceACLS: certServer.ca.connectorInfo:read,modify:allow (read) group="Enterprise KRA Administrators";allow (modify) group="Enterprise KRA Administrators" || group="Subsystem Group":Only Enterprise Administrators and Subsystem Group are allowed to update the connector information
 resourceACLS: certServer.ca.registerUser:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise RA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TKS Administrators" || group="Enterprise TPS Administrators":Only Enterprise Administrators are allowed to register a new agent
 resourceACLS: certServer.clone.configuration:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise RA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TKS Administrators":Only Enterprise Administrators are allowed to clone the configuration.
 resourceACLS: certServer.admin.ocsp:read,modify:allow (modify,read) group="Enterprise OCSP Administrators":Only Enterprise Administrators are allowed to read or update the OCSP configuration.
diff --git a/base/ca/shared/webapps/ca/WEB-INF/auth.properties b/base/ca/shared/webapps/ca/WEB-INF/auth.properties
index 08f5bea504dd16ceec3d2c08150e6cac7a18d597..b73b9ac10eaf5f07da61e78d85dd4d9c703d7064 100644
--- a/base/ca/shared/webapps/ca/WEB-INF/auth.properties
+++ b/base/ca/shared/webapps/ca/WEB-INF/auth.properties
@@ -8,6 +8,7 @@ account.login = certServer.ca.account,login
 account.logout = certServer.ca.account,logout
 admin.users = certServer.ca.users,execute
 admin.groups = certServer.ca.groups,execute
+admin.kraconnector = certServer.ca.connectorInfo,modify
 agent.certrequests = certServer.ca.certrequests,execute
 agent.certs = certServer.ca.certs,execute
 securityDomain.installToken = certServer.securitydomain.domainxml,read
diff --git a/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java b/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java
index bc6dd71a6ccaa8db16a91ea3cd5ab45326776acc..14b44e5d7695b230aa99c109d603594ee37a745c 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthorityApplication.java
@@ -13,6 +13,7 @@ import com.netscape.certsrv.base.PKIException;
 import com.netscape.cms.servlet.account.AccountService;
 import com.netscape.cms.servlet.admin.GroupMemberService;
 import com.netscape.cms.servlet.admin.GroupService;
+import com.netscape.cms.servlet.admin.KRAConnectorService;
 import com.netscape.cms.servlet.admin.SystemCertService;
 import com.netscape.cms.servlet.admin.UserCertService;
 import com.netscape.cms.servlet.admin.UserService;
@@ -50,6 +51,9 @@ public class CertificateAuthorityApplication extends Application {
         // system certs
         classes.add(SystemCertService.class);
 
+        // kra connector
+        classes.add(KRAConnectorService.class);
+
         // security domain
         IConfigStore cs = CMS.getConfigStore();
 
diff --git a/base/common/src/com/netscape/certsrv/connector/IConnector.java b/base/common/src/com/netscape/certsrv/connector/IConnector.java
index 61001be5bf907c3316427e6953ee335e259a9082..02e7231ab6d0e522997c6c59e91551c393d41e8e 100644
--- a/base/common/src/com/netscape/certsrv/connector/IConnector.java
+++ b/base/common/src/com/netscape/certsrv/connector/IConnector.java
@@ -58,4 +58,9 @@ public interface IConnector {
      * Starts this connector.
      */
     public void start();
+
+    /**
+     * Stop the connector.
+     */
+    public void stop();
 }
diff --git a/base/common/src/com/netscape/certsrv/connector/IResender.java b/base/common/src/com/netscape/certsrv/connector/IResender.java
index b1cd6149e8d95f687ff537981eac70161ad9394b..0b643c2726a8f1223a2ad81ba322092cbe65605b 100644
--- a/base/common/src/com/netscape/certsrv/connector/IResender.java
+++ b/base/common/src/com/netscape/certsrv/connector/IResender.java
@@ -36,4 +36,7 @@ public interface IResender extends Runnable {
      */
     public void addRequest(IRequest r);
 
+    public void start(final String name);
+
+    public void stop();
 }
diff --git a/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java b/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..f7b2c7246e1213ede9d4e70440b7b22123a7d08e
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/KRAConnectorClient.java
@@ -0,0 +1,54 @@
+// --- 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 java.net.URISyntaxException;
+
+import com.netscape.certsrv.client.ClientConfig;
+import com.netscape.certsrv.client.PKIClient;
+import com.netscape.certsrv.client.PKIConnection;
+
+/**
+ * @author Ade Lee
+ */
+public class KRAConnectorClient extends PKIClient {
+    public KRAConnectorResource kraConnectorClient;
+
+    public KRAConnectorClient(PKIConnection connection) throws URISyntaxException {
+        super(connection);
+        init();
+    }
+
+    public KRAConnectorClient(ClientConfig config) throws URISyntaxException {
+        super(config);
+        init();
+    }
+
+    public void init() throws URISyntaxException {
+        kraConnectorClient = createProxy(KRAConnectorResource.class);
+    }
+
+    public void addConnector(KRAConnectorInfo info) {
+        kraConnectorClient.addConnector(info);
+    }
+
+    public void removeConnector(String host, String port) {
+        kraConnectorClient.removeConnector(host, port);
+    }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/system/KRAConnectorInfo.java b/base/common/src/com/netscape/certsrv/system/KRAConnectorInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8caca601a482aaeca18a2a6cbd5962041f5a6ae
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/KRAConnectorInfo.java
@@ -0,0 +1,184 @@
+// --- 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 java.io.ByteArrayOutputStream;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Ade Lee
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class KRAConnectorInfo {
+    private static final String HOST = "host";
+    private static final String PORT = "port";
+    private static final String TRANSPORT_CERT= "transportCert";
+    private static final String URI = "uri";
+    private static final String TIMEOUT = "timeout";
+    private static final String LOCAL = "local";
+    private static final String ENABLE = "enable";
+
+    @XmlElement
+    String host;
+
+    @XmlElement
+    String port;
+
+    @XmlElement
+    String transportCert;
+
+    @XmlElement
+    String uri;
+
+    @XmlElement
+    String timeout;
+
+    @XmlElement
+    String local;
+
+    @XmlElement
+    String enable;
+
+    public KRAConnectorInfo() {
+        // needed for jaxb
+    }
+
+    public KRAConnectorInfo(MultivaluedMap<String, String> form) {
+        host = form.getFirst(HOST);
+        port = form.getFirst(PORT);
+        transportCert = form.getFirst(TRANSPORT_CERT);
+        uri = form.getFirst(URI);
+        timeout = form.getFirst(TIMEOUT);
+        local = form.getFirst(LOCAL);
+        enable = form.getFirst(ENABLE);
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getPort() {
+        return port;
+    }
+
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    public String getTransportCert() {
+        return transportCert;
+    }
+
+    public void setTransportCert(String transportCert) {
+        this.transportCert = transportCert;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout(String timeout) {
+        this.timeout = timeout;
+    }
+
+    public String getLocal() {
+        return local;
+    }
+
+    public void setLocal(String local) {
+        this.local = local;
+    }
+
+    public String getEnable() {
+        return enable;
+    }
+
+    public void setEnable(String enable) {
+        this.enable = enable;
+    }
+
+    public String toString() {
+        try {
+            JAXBContext context = JAXBContext.newInstance(KRAConnectorInfo.class);
+            Marshaller marshaller = context.createMarshaller();
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+            marshaller.marshal(this, stream);
+            return stream.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static void main(String args[]) throws Exception {
+        KRAConnectorInfo info = new KRAConnectorInfo();
+        info.setEnable("true");
+        info.setHost("host1.example.com");
+        info.setLocal("false");
+        info.setPort("8443");
+        info.setTimeout("30");
+        info.setUri("");
+        info.setTransportCert(
+            "MIIDnDCCAoSgAwIBAgIBDzANBgkqhkiG9w0BAQsFADBGMSMwIQYDVQQKExpyZWRo" +
+            "YXQuY29tIFNlY3VyaXR5IERvbWFpbjEfMB0GA1UEAxMWQ0EgU2lnbmluZyBDZXJ0" +
+            "aWZpY2F0ZTAeFw0xMzAxMDkyMTE5MDBaFw0xNDEyMzAyMTE5MDBaMEkxIzAhBgNV" +
+            "BAoTGnJlZGhhdC5jb20gU2VjdXJpdHkgRG9tYWluMSIwIAYDVQQDExlEUk0gVHJh" +
+            "bnNwb3J0IENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC" +
+            "AQEAqayxDggWH9Cld0O/j+HDfv7cLQexYiaDq/sEcFPYkREGisaxZggiovqLfMkz" +
+            "rSjutVtHuIEb3pU9frHYUjskbzdMbeU3nqDnA/ZPUw+YJe/6l19AbieADVB/L+6p" +
+            "TkNMwS/xsQIRnalYW9R4rebw3WiwQFxVHIorGL9qxUS5d12uguJokH/CbIML9Pek" +
+            "NgAZRGx87J4UkqTe5FImuEX8EwVWoW8Huc8QDthk1w5osz3jOTefwrJBEiI54d9F" +
+            "hl4O8ckXfecCAPYfn0Mi54I1VAbSRZEiq6GJ/xrN1IwLkaG7EmXtLU2IkaMz62MJ" +
+            "UmgBrlrtRj1eyAXLGwS4Fh4NVwIDAQABo4GRMIGOMB8GA1UdIwQYMBaAFMjscbmB" +
+            "k0Gz2wVxGWkn9bjSA88wMEYGCCsGAQUFBwEBBDowODA2BggrBgEFBQcwAYYqaHR0" +
+            "cDovL2FsZWUtd29ya3BjLnJlZGhhdC5jb206ODI4MC9jYS9vY3NwMA4GA1UdDwEB" +
+            "/wQEAwIE8DATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEA" +
+            "gCCPZ5+pkxZDgKJpisJ8/5TfKtN/q5pO8CNKIM9Cz78ucGEaR2lzJVH5EOdO2ZM6" +
+            "y+5AhK2hcKifNI3DPAfYdYsSVBR6Mrij4/aAMZlqtKjlNs/LJ2TdKGRxxYsEAQL+" +
+            "OToCfXijDh0kzQ9oSII+9fBCWljkq/K89bSGcwR/y1v+ll+z9Wci+QAFKUzmqZyL" +
+            "eEbOOmYhgvVSnYV1XdB6lbWQOOdpytvECl1UaQUSsDfJkk8mH1Fkl0dnrChh7mXM" +
+            "2ZBYwBsI2DhAyWBKQgQfgxQwxmobbg6BVnn9/CW7gJ0Gwb+VJEvRtaBOnjliP74/" +
+            "Jb+fenCZE47zRNCDubBe+Q==");
+
+        System.out.println(info);
+    }
+}
+
diff --git a/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java b/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..01f159e965fb2482f42931e92f87444b82394eb7
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/system/KRAConnectorResource.java
@@ -0,0 +1,55 @@
+// --- 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.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import com.netscape.certsrv.acls.ACLMapping;
+
+/**
+ * @author Ade Lee
+ */
+@Path("admin/kraconnector")
+@ACLMapping("admin.kraconnector")
+public interface KRAConnectorResource {
+
+    @POST
+    @Path("add")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    public void addConnector(KRAConnectorInfo info);
+
+    @POST
+    @Path("add")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
+    public void addConnector(MultivaluedMap<String, String> form);
+
+    @POST
+    @Path("remove")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
+    public void removeConnector(@FormParam("host") String host, @FormParam("port") String port);
+
+}
diff --git a/base/common/src/com/netscape/cms/servlet/admin/KRAConnectorProcessor.java b/base/common/src/com/netscape/cms/servlet/admin/KRAConnectorProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ac54b20f3846fd7e7168ab61cf679dddb020054
--- /dev/null
+++ b/base/common/src/com/netscape/cms/servlet/admin/KRAConnectorProcessor.java
@@ -0,0 +1,223 @@
+// --- 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.cms.servlet.admin;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.BadRequestException;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.EPropertyNotFound;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.ca.ICAService;
+import com.netscape.certsrv.ca.ICertificateAuthority;
+import com.netscape.certsrv.connector.IConnector;
+import com.netscape.certsrv.system.KRAConnectorInfo;
+import com.netscape.cms.servlet.processors.Processor;
+
+/**
+ * @author Ade Lee
+ */
+public class KRAConnectorProcessor extends Processor {
+    private boolean connectorExists = false;
+
+    // Connector constants
+    public final static String PREFIX = "ca.connector.KRA";
+
+    public KRAConnectorProcessor(Locale locale) throws EPropertyNotFound, EBaseException {
+        super("kraconnector", locale);
+        ICertificateAuthority ca = (ICertificateAuthority)CMS.getSubsystem("ca");
+        ICAService caService = (ICAService)ca.getCAService();
+        connectorExists = (caService.getKRAConnector() != null)? true:false;
+    }
+
+    public void removeConnector(String newHost, String newPort) throws EPropertyNotFound, EBaseException {
+        if (! connectorExists) {
+            CMS.debug("removeConnector: no KRA connector exists, returning success");
+            return;
+        }
+
+        if ((newHost == null) || (newPort == null)) {
+            CMS.debug("removeConnector: malformed request.  newHost or newPort is null");
+            throw new BadRequestException("Bad Request: KRA Host or Port not defined");
+        }
+        IConfigStore cs = CMS.getConfigStore();
+        String host = cs.getString(PREFIX + ".host");
+        String port = cs.getString(PREFIX + ".port");
+
+        if ((host == null) || (port == null)) {
+            CMS.debug("removeConnector: bad connector configuration - host or port are null");
+            throw new PKIException("Bad Connector configuration on this CA");
+        }
+
+        String hostport = newHost + ":" + newPort;
+        if ((host.equals(newHost)) && port.equals(newPort)) {
+            CMS.debug("removeConnector: Removing " + PREFIX + " substore");
+            cs.removeSubStore(PREFIX);
+            cs.commit(true);
+            deleteConnector();
+        } else if (host.indexOf(' ') != -1) { // host is a list
+            String[] hostList = host.trim().split(" ");
+            ArrayList<String> finalList = new ArrayList<String>();
+            for (String h : hostList) {
+                if (! h.equals(hostport)) {
+                    finalList.add(h);
+                }
+            }
+            if (finalList.size() == hostList.length) {
+                CMS.debug("removeConnector: no connector for " + hostport + " exists. Returning success");
+                return;
+            }
+
+            CMS.debug("removeConnector: Removing " + hostport + " from " + PREFIX);
+
+            if (finalList.size() == 0) {
+                CMS.debug("removeConnector: Removing " + PREFIX + " substore");
+                cs.removeSubStore(PREFIX);
+                cs.commit(true);
+                deleteConnector();
+            } else if (finalList.size() == 1) {
+                cs.putString(PREFIX + ".host", finalList.get(0).split(":")[0]);
+                cs.putString(PREFIX + ".port", finalList.get(0).split(":")[1]);
+                cs.commit(true);
+                replaceConnector();
+            } else {
+                String finalString = "";
+                for (String h : finalList)
+                    finalString += h + " ";
+                cs.putString(PREFIX + ".host", finalString.trim());
+                cs.commit(true);
+                replaceConnector();
+            }
+        } else {
+            CMS.debug("removeConnector: no connector for " + hostport + " exists. Returning success");
+        }
+    }
+
+    public void stopConnector() {
+        ICertificateAuthority ca = (ICertificateAuthority)CMS.getSubsystem("ca");
+        ICAService caService = (ICAService)ca.getCAService();
+        IConnector kraConnector = caService.getKRAConnector();
+        if (kraConnector != null) {
+            kraConnector.stop();
+        }
+    }
+
+    public void startConnector() {
+        ICertificateAuthority ca = (ICertificateAuthority)CMS.getSubsystem("ca");
+        ICAService caService = (ICAService)ca.getCAService();
+        IConnector kraConnector = caService.getKRAConnector();
+        if (kraConnector != null) {
+            kraConnector.start();
+        }
+    }
+
+    public void replaceConnector() throws EBaseException {
+        // stop the old connector
+        stopConnector();
+
+        ICertificateAuthority ca = (ICertificateAuthority)CMS.getSubsystem("ca");
+        ICAService caService = (ICAService)ca.getCAService();
+        IConfigStore cs = CMS.getConfigStore();
+
+        IConnector kraConnector = caService.getConnector(cs.getSubStore(PREFIX));
+        caService.setKRAConnector(kraConnector);
+
+        startConnector();
+    }
+
+    public void deleteConnector() {
+        stopConnector();
+
+        ICertificateAuthority ca = (ICertificateAuthority)CMS.getSubsystem("ca");
+        ICAService caService = (ICAService)ca.getCAService();
+        caService.setKRAConnector(null);
+    }
+
+    public void addConnector(KRAConnectorInfo info) throws EPropertyNotFound, EBaseException {
+        IConfigStore cs = CMS.getConfigStore();
+        String newHost = info.getHost();
+        String newPort = info.getPort();
+        String newTransportCert = info.getTransportCert();
+
+        if ((newHost == null) || (newPort == null) || (newTransportCert == null)) {
+            CMS.debug("addConnector: malformed request.  newHost, newPort or transport cert is null");
+            throw new BadRequestException("Bad Request: KRA host, port or transport cert not defined");
+        }
+
+        if (connectorExists) {
+            String host = cs.getString(PREFIX + ".host");
+            String port = cs.getString(PREFIX + ".port");
+
+            if ((!host.equals(newHost)) || (!port.equals(newPort))) { //existing connector is not the same
+
+                // check transport cert
+                String transportCert = cs.getString(PREFIX + ".transportCert");
+                if (!transportCert.equals(newTransportCert)) {
+                    CMS.debug("addConnector: Connector is already defined");
+                    throw new BadRequestException("KRA connector has already been defined for this CA");
+                }
+
+                String hostport = newHost + ":" + newPort;
+                if (host.indexOf(' ') != -1) { // host is a list
+                    String[] hostList = host.trim().split(" ");
+                    for (String h : hostList) {
+                        if (h.equals(hostport)) {
+                            CMS.debug("addConnector: connector for " + hostport +
+                                    " is already present.  Returning success");
+                            return;
+                        }
+                    }
+
+                    CMS.debug("addConnector: adding " + hostport + " to KRA connector host list");
+                    cs.putString(PREFIX + ".host", host + " " + hostport);
+                    cs.commit(true);
+                    replaceConnector();
+                    return;
+                } else { // host is not a list, turn it into one
+                    CMS.debug("addConnector: adding " + hostport + " to KRA connector");
+                    cs.putString(PREFIX + ".host", host + ":" + port + " " + hostport);
+                    cs.commit(true);
+                    replaceConnector();
+                    return;
+                }
+            }
+        }
+
+        // connector does not exist, or existing connector is the same host/port and we are replacing it
+        cs.putString(PREFIX + ".host", info.getHost());
+        cs.putString(PREFIX + ".port", info.getPort());
+        cs.putString(PREFIX + ".enable", info.getEnable() != null ? info.getEnable() : "true");
+        cs.putString(PREFIX + ".local", info.getLocal() != null ? info.getLocal(): "false");
+        cs.putString(PREFIX + ".timeout", info.getTimeout() != null ?  info.getTimeout() : "30");
+        cs.putString(PREFIX + ".uri", info.getUri() != null ? info.getUri() : "/kra/agent/kra/connector");
+        cs.putString(PREFIX + ".transportCert", info.getTransportCert());
+
+        String nickname = cs.getString("ca.subsystem.nickname", "");
+        String tokenname = cs.getString("ca.subsystem.tokenname", "");
+        if (!tokenname.equals("Internal Key Storage Token"))
+            nickname = tokenname + ":" + nickname;
+        cs.putString(PREFIX + ".nickName", nickname);
+        cs.commit(true);
+
+        replaceConnector();
+    }
+
+}
diff --git a/base/common/src/com/netscape/cms/servlet/admin/KRAConnectorService.java b/base/common/src/com/netscape/cms/servlet/admin/KRAConnectorService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1d0b07c25228e6d285194c419ad860160d40e6f
--- /dev/null
+++ b/base/common/src/com/netscape/cms/servlet/admin/KRAConnectorService.java
@@ -0,0 +1,61 @@
+// --- 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.cms.servlet.admin;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.PKIException;
+import com.netscape.certsrv.system.KRAConnectorInfo;
+import com.netscape.certsrv.system.KRAConnectorResource;
+import com.netscape.cms.servlet.base.PKIService;
+
+/**
+ * @author Ade Lee
+ */
+public class KRAConnectorService extends PKIService implements KRAConnectorResource {
+
+    @Override
+    public void addConnector(KRAConnectorInfo info) {
+        try {
+            KRAConnectorProcessor processor = new KRAConnectorProcessor(getLocale());
+            processor.addConnector(info);
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
+
+    @Override
+    public void removeConnector(String host, String port) {
+        try {
+            KRAConnectorProcessor processor = new KRAConnectorProcessor(getLocale());
+            processor.removeConnector(host, port);
+        } catch (EBaseException e) {
+            e.printStackTrace();
+            throw new PKIException(e.getMessage());
+        }
+    }
+
+    @Override
+    public void addConnector(MultivaluedMap<String, String> form) {
+        KRAConnectorInfo info = new KRAConnectorInfo(form);
+        addConnector(info);
+    }
+
+}
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java
index b62e184b7bb01b550507617700ee182d90477bb8..efc0d5d34c9067aab8baf466731af4b6b909467f 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java
@@ -18,7 +18,6 @@
 package com.netscape.cms.servlet.csadmin;
 
 import java.io.IOException;
-import java.util.Enumeration;
 import java.util.Locale;
 
 import javax.servlet.ServletConfig;
@@ -26,6 +25,7 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.jboss.resteasy.spi.BadRequestException;
 import org.w3c.dom.Node;
 
 import com.netscape.certsrv.apps.CMS;
@@ -33,11 +33,10 @@ import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.authorization.AuthzToken;
 import com.netscape.certsrv.authorization.EAuthzAccessDenied;
 import com.netscape.certsrv.base.EBaseException;
-import com.netscape.certsrv.base.IConfigStore;
-import com.netscape.certsrv.ca.ICAService;
-import com.netscape.certsrv.ca.ICertificateAuthority;
-import com.netscape.certsrv.connector.IConnector;
+import com.netscape.certsrv.base.PKIException;
 import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.system.KRAConnectorInfo;
+import com.netscape.cms.servlet.admin.KRAConnectorProcessor;
 import com.netscape.cms.servlet.base.CMSServlet;
 import com.netscape.cms.servlet.base.UserInfo;
 import com.netscape.cms.servlet.common.CMSRequest;
@@ -46,9 +45,6 @@ import com.netscape.cmsutil.xml.XMLObject;
 
 public class UpdateConnector extends CMSServlet {
 
-    /**
-     *
-     */
     private static final long serialVersionUID = 972871860008509849L;
     private final static String SUCCESS = "0";
     private final static String FAILED = "1";
@@ -69,6 +65,18 @@ public class UpdateConnector extends CMSServlet {
         CMS.debug("UpdateConnector: done initializing...");
     }
 
+    public KRAConnectorInfo createConnectorInfo(HttpServletRequest httpReq) {
+        KRAConnectorInfo info = new KRAConnectorInfo();
+        info.setHost(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".host"));
+        info.setPort(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".port"));
+        info.setTimeout(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".timeout"));
+        info.setTransportCert(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".transportCert"));
+        info.setUri(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".uri"));
+        info.setLocal(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".local"));
+        info.setEnable(httpReq.getParameter(KRAConnectorProcessor.PREFIX + ".enable"));
+        return info;
+    }
+
     /**
      * Process the HTTP request.
      */
@@ -122,47 +130,24 @@ public class UpdateConnector extends CMSServlet {
             return;
         }
 
-        // check if connector exists
-        ICertificateAuthority ca = (ICertificateAuthority)CMS.getSubsystem("ca");
-        ICAService caService = (ICAService)ca.getCAService();
-        boolean connectorExists = (caService.getKRAConnector() != null)? true:false;
-        if (connectorExists) {
-            CMS.debug("UpdateConnector: KRA connector already exists");
-        } else {
-            IConfigStore cs = CMS.getConfigStore();
+        String op = httpReq.getParameter("op");
+        if (op == null) {
+            op="add";
+        }
 
-            @SuppressWarnings("unchecked")
-            Enumeration<String> list = httpReq.getParameterNames();
-            while (list.hasMoreElements()) {
-                String name = list.nextElement();
-                String val = httpReq.getParameter(name);
-                if (name != null && name.startsWith("ca.connector")) {
-                    CMS.debug("Adding connector update name=" + name + " val=" + val);
-                    cs.putString(name, val);
-                } else {
-                    CMS.debug("Skipping connector update name=" + name + " val=" + val);
-                }
-            }
-
-            try {
-                String nickname = cs.getString("ca.subsystem.nickname", "");
-                String tokenname = cs.getString("ca.subsystem.tokenname", "");
-                if (!tokenname.equals("Internal Key Storage Token"))
-                    nickname = tokenname + ":" + nickname;
-                cs.putString("ca.connector.KRA.nickName", nickname);
-                cs.commit(false);
-            } catch (Exception e) {
-            }
-
-            // start the connector
-            try {
-                IConnector kraConnector = caService.getConnector(
-                        cs.getSubStore("ca.connector.KRA"));
-                caService.setKRAConnector(kraConnector);
-                kraConnector.start();
-            } catch (Exception e) {
-                CMS.debug("Failed to start connector " + e);
+        String status = SUCCESS;
+        String error = "";
+        KRAConnectorProcessor processor = new KRAConnectorProcessor(getLocale(httpReq));
+        KRAConnectorInfo info = createConnectorInfo(httpReq);
+        try {
+            if (op.equals("add")) {
+                processor.addConnector(info);
+            } else {
+                processor.removeConnector(info.getHost(), info.getPort());
             }
+        } catch (BadRequestException | PKIException e) {
+            status = FAILED;
+            error = e.getMessage();
         }
 
         // send success status back to the requestor
@@ -170,13 +155,13 @@ public class UpdateConnector extends CMSServlet {
             CMS.debug("UpdateConnector: Sending response");
             XMLObject xmlObj = new XMLObject();
             Node root = xmlObj.createRoot("XMLResponse");
-
-            if (connectorExists) {
-                xmlObj.addItemToContainer(root, "Status", FAILED);
-                xmlObj.addItemToContainer(root, "Error", "DRM connector already exists.");
-            } else {
+            if (status.equals(SUCCESS)) {
                 xmlObj.addItemToContainer(root, "Status", SUCCESS);
+            } else {
+                xmlObj.addItemToContainer(root, "Status", FAILED);
+                xmlObj.addItemToContainer(root, "Error", error);
             }
+
             byte[] cb = xmlObj.toByteArray();
 
             outputResult(httpResp, "application/xml", cb);
diff --git a/base/common/src/com/netscape/cmscore/connector/HttpConnector.java b/base/common/src/com/netscape/cmscore/connector/HttpConnector.java
index f947164d96c999893f953c6af8f34962c8927beb..33b0d62b76a34238961ae439c546aab521bbd34e 100644
--- a/base/common/src/com/netscape/cmscore/connector/HttpConnector.java
+++ b/base/common/src/com/netscape/cmscore/connector/HttpConnector.java
@@ -43,7 +43,6 @@ public class HttpConnector implements IConnector {
     // XXX todo make this a pool.
     // XXX use CMMF in the future.
     protected IHttpConnection mConn = null;
-    private Thread mResendThread = null;
     private IResender mResender = null;
     @SuppressWarnings("unused")
     private int mTimeout;
@@ -73,7 +72,6 @@ public class HttpConnector implements IConnector {
         //        mConn = CMS.getHttpConnection(dest, mFactory);
         // this will start resending past requests in parallel.
         mResender = CMS.getResender(mSource, nickName, dest, resendInterval);
-        mResendThread = new Thread(mResender, "HttpConnector");
     }
 
     // Inserted by beomsuk
@@ -98,7 +96,6 @@ public class HttpConnector implements IConnector {
 
         // this will start resending past requests in parallel.
         mResender = CMS.getResender(mSource, nickName, dest, resendInterval);
-        mResendThread = new Thread(mResender, "HttpConnector");
     }
 
     // Insert end
@@ -202,7 +199,13 @@ public class HttpConnector implements IConnector {
     }
 
     public void start() {
-        mResendThread.start();
+        CMS.debug("Starting HttpConnector resender thread");
+        mResender.start("HttpConnector");
+    }
+
+    public void stop() {
+        CMS.debug("Stopping HttpConnector resender thread");
+        mResender.stop();
     }
 
 }
diff --git a/base/common/src/com/netscape/cmscore/connector/LocalConnector.java b/base/common/src/com/netscape/cmscore/connector/LocalConnector.java
index c4eb78625e9fa544db2ebb4de16ec05bc9052ddb..ba2db83a137010f49531cc9c314cf030b2a244e6 100644
--- a/base/common/src/com/netscape/cmscore/connector/LocalConnector.java
+++ b/base/common/src/com/netscape/cmscore/connector/LocalConnector.java
@@ -204,6 +204,9 @@ public class LocalConnector implements IConnector {
     public void start() {
     }
 
+    public void stop() {
+    }
+
     protected void transferRequest(IRequest src, IRequest dest) {
         RequestTransfer.transfer(src, dest);
     }
diff --git a/base/common/src/com/netscape/cmscore/connector/Resender.java b/base/common/src/com/netscape/cmscore/connector/Resender.java
index e1b19749a8b6edaa345470d3f3ed1763df9b10f6..a949b993e4765d02b5c8d5a327b4a1740417309f 100644
--- a/base/common/src/com/netscape/cmscore/connector/Resender.java
+++ b/base/common/src/com/netscape/cmscore/connector/Resender.java
@@ -20,6 +20,10 @@ package com.netscape.cmscore.connector;
 import java.io.IOException;
 import java.util.Enumeration;
 import java.util.Vector;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.authority.IAuthority;
@@ -40,14 +44,12 @@ import com.netscape.cmsutil.http.JssSSLSocketFactory;
  * Default interval is 5 minutes.
  */
 public class Resender implements IResender {
-    public static final int SECOND = 1000; //milliseconds
-    public static final int MINUTE = 60 * SECOND;
-    public static final int HOUR = 60 * MINUTE;
-    public static final int DAY = 24 * HOUR;
+    public static final int MINUTE = 60;
 
     protected IAuthority mAuthority = null;
     IRequestQueue mQueue = null;
     protected IRemoteAuthority mDest = null;
+    ScheduledExecutorService executorService;
 
     /* Vector of Request Id *Strings* */
     protected Vector<String> mRequestIds = new Vector<String>();
@@ -55,6 +57,7 @@ public class Resender implements IResender {
     protected HttpConnection mConn = null;
 
     protected String mNickName = null;
+    protected boolean connected = false;
 
     // default interval.
     // XXX todo add another interval for requests unsent because server
@@ -66,9 +69,6 @@ public class Resender implements IResender {
         mQueue = mAuthority.getRequestQueue();
         mDest = dest;
         mNickName = nickName;
-
-        //mConn = new HttpConnection(dest,
-        //           new JssSSLSocketFactory(nickName));
     }
 
     public Resender(
@@ -77,11 +77,9 @@ public class Resender implements IResender {
         mAuthority = authority;
         mQueue = mAuthority.getRequestQueue();
         mDest = dest;
+        mNickName = nickName;
         if (interval > 0)
-            mInterval = interval * SECOND; // interval specified in seconds.
-
-        //mConn = new HttpConnection(dest,
-        //           new JssSSLSocketFactory(nickName));
+            mInterval = interval; // interval specified in seconds.
     }
 
     // must be done after a subsystem 'start' so queue is initialized.
@@ -93,9 +91,7 @@ public class Resender implements IResender {
 
         while (list != null && list.hasMoreElements()) {
             RequestId rid = list.nextRequestId();
-
-            CMS.debug(
-                    "added request Id " + rid + " in init to resend queue.");
+            CMS.debug("added request Id " + rid + " in init to resend queue.");
             // note these are added as strings
             mRequestIds.addElement(rid.toString());
         }
@@ -106,26 +102,38 @@ public class Resender implements IResender {
             // note the request ids are added as strings.
             mRequestIds.addElement(r.getRequestId().toString());
         }
-        CMS.debug(
-                "added " + r.getRequestId() + " to resend queue");
+        CMS.debug("added " + r.getRequestId() + " to resend queue");
+    }
+
+    public void start(final String name) {
+        CMS.debug("Starting resender thread with interval " + mInterval);
+
+        // schedule task to run immediately and repeat after specified interval
+        executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+            public Thread newThread(Runnable r) {
+                return new Thread(r, name);
+            }
+        });
+        executorService.scheduleWithFixedDelay(this, 0, mInterval, TimeUnit.SECONDS);
+
     }
 
     public void run() {
+        if (! CMS.isInRunningState())
+            return;
 
-        CMS.debug("Resender: In resender Thread run:");
-        mConn = new HttpConnection(mDest,
-                    new JssSSLSocketFactory(mNickName));
-        initRequests();
+        if (! connected) {
+            CMS.debug("Connecting ...");
+            mConn = new HttpConnection(mDest, new JssSSLSocketFactory(mNickName));
+            initRequests();
+            connected = true;
+        }
+        resend();
+    }
 
-        do {
-            resend();
-            try {
-                Thread.sleep(mInterval);
-            } catch (InterruptedException e) {
-                mAuthority.log(ILogger.LL_INFO, CMS.getLogMessage("CMSCORE_CONNECTOR_RESENDER_INTERRUPTED"));
-                continue;
-            }
-        } while (true);
+    public void stop() {
+        // shutdown executorService without interrupting running task
+        if (executorService != null) executorService.shutdown();
     }
 
     private void resend() {
diff --git a/base/deploy/src/scriptlets/initialization.py b/base/deploy/src/scriptlets/initialization.py
index 034179ef1fda3ae0c4647f474cd7eef86e9114c6..102fd42455ae0a61b44b955c829cc407bc8c658d 100644
--- a/base/deploy/src/scriptlets/initialization.py
+++ b/base/deploy/src/scriptlets/initialization.py
@@ -104,6 +104,10 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         util.identity.set_gid(master['pki_group'])
         # get ports to remove selinux context
         util.configuration_file.populate_non_default_ports()
+
+        # remove kra connector from CA if this is a KRA
+        util.kra_connector.deregister()
+
         # de-register instance from its Security Domain
         #
         #     NOTE:  Since the security domain of an instance must be up
diff --git a/base/deploy/src/scriptlets/pkihelper.py b/base/deploy/src/scriptlets/pkihelper.py
index 48c5f84f0ab4d8074b822005bc96b0a776f6331d..c58fcf0e886fee711297d0591a2e9127ee801d2f 100644
--- a/base/deploy/src/scriptlets/pkihelper.py
+++ b/base/deploy/src/scriptlets/pkihelper.py
@@ -1983,6 +1983,28 @@ class password:
                 sys.exit(1)
         return
 
+    def get_password(self, path, token_name, critical_failure=True):
+        if os.path.exists(path) and os.path.isfile(path) and\
+           os.access(path, os.R_OK):
+            tokens = PKIConfigParser.read_simple_configuration_file(path)
+            hardware_token = "hardware-" + token_name
+            if tokens.has_key(hardware_token):
+                token_name = hardware_token
+                token_pwd = tokens[hardware_token]
+            elif tokens.has_key(token_name):
+                   token_pwd = tokens[token_name]
+
+        if token_pwd is None or token_pwd == '':
+            # TODO prompt for this password
+            config.pki_log.error(log.PKIHELPER_PASSWORD_NOT_FOUND_1,
+                                 token_name,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(-1)
+            else:
+                return
+        return token_pwd
+
 
 # PKI Deployment NSS 'certutil' Class
 class certutil:
@@ -2260,6 +2282,136 @@ class certutil:
                 sys.exit(1)
         return
 
+# KRA Connector Class
+class kra_connector:
+    def deregister(self, critical_failure=False):
+        try:
+            # this is applicable to KRAs only
+            if master['pki_subsystem_type'] != "kra":
+                return
+
+            config.pki_log.info(
+                log.PKIHELPER_KRACONNECTOR_UPDATE_CONTACT,
+                extra=config.PKI_INDENTATION_LEVEL_2)
+
+            cs_cfg = PKIConfigParser.read_simple_configuration_file(
+                         master['pki_target_cs_cfg'])
+            krahost = cs_cfg.get('service.machineName')
+            kraport = cs_cfg.get('pkicreate.secure_port')
+            cahost = cs_cfg.get('cloning.ca.hostname')
+            caport = cs_cfg.get('cloning.ca.httpsport')
+            if cahost is None or\
+               caport is None:
+                config.pki_log.warning(
+                    log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.error(
+                    log.PKIHELPER_UNDEFINED_CA_HOST_PORT,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(-1)
+                else:
+                    return
+
+            # retrieve subsystem nickname
+            subsystemnick = cs_cfg.get('kra.cert.subsystem.nickname')
+            if subsystemnick is None:
+                config.pki_log.warning(
+                    log.PKIHELPER_KRACONNECTOR_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:
+                    sys.exit(-1)
+                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 = password.get_password(
+                            master['pki_shared_password_conf'],
+                            token_name,
+                            critical_failure)
+
+            if token_pwd is None or token_pwd == '':
+                config.pki_log.warning(
+                    log.PKIHELPER_KRACONNECTOR_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:
+                    sys.exit(-1)
+                else:
+                    return
+
+            self.execute_using_sslget(caport, cahost, subsystemnick, 
+                                 token_pwd, krahost, kraport)
+
+        except subprocess.CalledProcessError as exc:
+            config.pki_log.warning(
+                log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE_2,
+                str(krahost),
+                str(kraport),
+                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:
+                sys.exit(-1)
+        return
+
+    def execute_using_pki(self, caport, cahost, subsystemnick, 
+      token_pwd, krahost, kraport, critical_failure=False):
+        command = "/bin/pki -p '{}' -h '{}' -n '{}' -P https -d '{}' -w '{}' "\
+                  "kraconnector-del {} {}".format(
+                      caport, cahost, subsystemnick,
+                      master['pki_database_path'],
+                      token_pwd, krahost, kraport)
+
+        output = subprocess.check_output(command,
+                                         stderr=subprocess.STDOUT,
+                                         shell=True)
+
+        error = re.findall("ClientResponseFailure:(.*?)", output)
+        if error:
+            config.pki_log.warning(
+                log.PKIHELPER_KRACONNECTOR_UPDATE_FAILURE_2,
+                str(krahost),
+                str(kraport),
+                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:
+            sys.exit(-1)
+
+    def execute_using_sslget(self, caport, cahost, subsystemnick, 
+      token_pwd, krahost, kraport):
+        urlheader = "https://{}:{}".format(cahost, caport)
+        updateURL = "/ca/rest/admin/kraconnector/remove"
+
+        params = "host=" + str(krahost) +\
+                 "&port=" + str(kraport)
+
+        command = "/usr/bin/sslget -n '{}' -p '{}' -d '{}' -e '{}' "\
+                  "-v -r '{}' {}:{} 2>&1".format(
+                      subsystemnick, token_pwd,
+                      master['pki_database_path'],
+                      params, updateURL,
+                      cahost, caport)
+
+        # update KRA connector
+        # Execute this "sslget" command
+        # Note that sslget will return non-zero value for HTTP code != 200
+        # and this will raise an exception
+        output = subprocess.check_output(command,
+                                         stderr=subprocess.STDOUT,
+                                         shell=True)
 
 # PKI Deployment Security Domain Class
 class security_domain:
@@ -2337,33 +2489,16 @@ class security_domain:
                 urladminheader = "https://{}:{}".format(sechost, secadminport)
                 updateURL = "/ca/agent/ca/updateDomainXML"
 
-                # process this PKI subsystem instance's 'password.conf'
-                #
-                #     REMINDER:  NEVER log this 'sensitive' information!
-                #
-                if os.path.exists(master['pki_shared_password_conf']) and\
-                   os.path.isfile(master['pki_shared_password_conf']) and\
-                   os.access(master['pki_shared_password_conf'], os.R_OK):
-                    tokens = PKIConfigParser.read_simple_configuration_file(
-                                 master['pki_shared_password_conf'])
-                    hardware_token = "hardware-" + token_name
-                    if tokens.has_key(hardware_token):
-                        token_name = hardware_token
-                        token_pwd = tokens[hardware_token]
-                    elif tokens.has_key(token_name):
-                        token_pwd = tokens[token_name]
-
+                token_pwd = password.get_password(
+                                master['pki_shared_password_conf'],
+                                token_name,
+                                critical_failure)
                 if token_pwd is None or token_pwd == '':
-                    # 'pkiremove' prompts with
-                    # "What is the password for this token?"
                     config.pki_log.warning(
                         log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
                         typeval,
                         secname,
                         extra=config.PKI_INDENTATION_LEVEL_2)
-                    config.pki_log.error(log.PKIHELPER_PASSWORD_NOT_FOUND_1,
-                                         token_name,
-                                         extra=config.PKI_INDENTATION_LEVEL_2)
                     if critical_failure == True:
                         sys.exit(-1)
                     else:
@@ -2608,5 +2743,6 @@ war = war()
 password = password()
 certutil = certutil()
 security_domain = security_domain()
+kra_connector = kra_connector()
 systemd = systemd()
 jython = jython()
diff --git a/base/deploy/src/scriptlets/pkimessages.py b/base/deploy/src/scriptlets/pkimessages.py
index ade19422e9033146d576dcb60fb5648f442adf35..3a6bec1e389f6f282cd4f6a2f95386fdeb26753b 100644
--- a/base/deploy/src/scriptlets/pkimessages.py
+++ b/base/deploy/src/scriptlets/pkimessages.py
@@ -194,6 +194,10 @@ PKIHELPER_IS_A_DIRECTORY_1 = "'%s' is a directory"
 PKIHELPER_IS_A_FILE_1 = "'%s' is a file"
 PKIHELPER_IS_A_SYMLINK_1 = "'%s' is a symlink"
 PKIHELPER_JAR_XF_C_2 = "jar -xf %s -C %s"
+PKIHELPER_KRACONNECTOR_UPDATE_CONTACT =\
+    "contacting the CA to update the KRA connector"
+PKIHELPER_KRACONNECTOR_UPDATE_FAILURE = "Failed to update KRA connector on CA"
+PKIHELPER_KRACONNECTOR_UPDATE_FAILURE_2 = "Failed to update KRA connector for %s:%s"
 PKIHELPER_LINK_S_2 = "ln -s %s %s"
 PKIHELPER_MKDIR_1 = "mkdir -p %s"
 PKIHELPER_MODIFY_DIR_1 = "modifying '%s'"
@@ -259,12 +263,14 @@ PKIHELPER_TOMCAT_INSTANCES_2 = "PKI Tomcat registry '%s' contains '%d' "\
                                "Tomcat PKI instances"
 PKIHELPER_TOUCH_1 = "touch %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 =\
     "Either a value for '%s' MUST be defined in '%s', or "\
     "the randomly generated client pin MUST be used"
 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_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' . . ."
 PKIHELPER_USER_ADD_DEFAULT_2 = "adding default UID '%s' for user '%s' . . ."
diff --git a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
index 2e661fcc6c152d9cb88c14b161b2bed0f8172223..191a6326da7c53475514ff650f41fe61f0a257bd 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
@@ -36,6 +36,7 @@ import com.netscape.certsrv.client.PKIConnection;
 import com.netscape.cmstools.cert.CertCLI;
 import com.netscape.cmstools.group.GroupCLI;
 import com.netscape.cmstools.key.KeyCLI;
+import com.netscape.cmstools.system.KRAConnectorCLI;
 import com.netscape.cmstools.system.SecurityDomainCLI;
 import com.netscape.cmstools.user.UserCLI;
 
@@ -55,6 +56,7 @@ public class MainCLI extends CLI {
         addModule(new CertCLI(this));
         addModule(new GroupCLI(this));
         addModule(new KeyCLI(this));
+        addModule(new KRAConnectorCLI(this));
         addModule(new SecurityDomainCLI(this));
         addModule(new UserCLI(this));
     }
diff --git a/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorAddCLI.java b/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorAddCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc163341b0448ea13b8e9ab8d49ba9613fd5f30a
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorAddCLI.java
@@ -0,0 +1,77 @@
+// --- 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.io.FileInputStream;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Unmarshaller;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.ParseException;
+
+import com.netscape.certsrv.system.KRAConnectorInfo;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Ade Lee
+ */
+public class KRAConnectorAddCLI extends CLI {
+    public KRAConnectorCLI parent;
+
+    public KRAConnectorAddCLI(KRAConnectorCLI parent) {
+        super("add", "Add KRA Connector");
+        this.parent = parent;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(parent.name + "-" + name + " <File Name>", options);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        CommandLine cmd = null;
+
+        try {
+            cmd = parser.parse(options, args);
+        } catch (ParseException e) {
+            System.err.println("Error: " + e.getMessage());
+            printHelp();
+            System.exit(-1);
+        }
+
+        String[] cLineArgs = cmd.getArgs();
+
+        if (cLineArgs.length < 1) {
+            System.err.println("Error: No file name specified.");
+            printHelp();
+            System.exit(-1);
+        }
+
+        FileInputStream fis = new FileInputStream(cLineArgs[0].trim());
+
+        JAXBContext context = JAXBContext.newInstance(KRAConnectorInfo.class);
+        Unmarshaller unmarshaller = context.createUnmarshaller();
+        KRAConnectorInfo info = (KRAConnectorInfo) unmarshaller.unmarshal(fis);
+
+        parent.client.addConnector(info);
+
+        MainCLI.printMessage("Added KRA Connector");
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorCLI.java b/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..d59d7445ad4c1d9da88d3087b0f262153a3cc5d0
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorCLI.java
@@ -0,0 +1,92 @@
+// --- 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.apache.commons.lang.StringUtils;
+
+import com.netscape.certsrv.system.KRAConnectorClient;
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+/**
+ * @author Ade Lee
+ */
+public class KRAConnectorCLI extends CLI {
+
+    public MainCLI parent;
+    public KRAConnectorClient client;
+
+    public KRAConnectorCLI(MainCLI parent) {
+        super("kraconnector", "KRA Connector management commands");
+        this.parent = parent;
+        addModule(new KRAConnectorAddCLI(this));
+        addModule(new KRAConnectorRemoveCLI(this));
+    }
+
+    public void printHelp() {
+
+        System.out.println("Commands:");
+
+        int leftPadding = 1;
+        int rightPadding = 25;
+
+        for (CLI module : modules.values()) {
+            String label = name+"-"+module.getName();
+
+            int padding = rightPadding - leftPadding - label.length();
+            if (padding < 1) padding = 1;
+
+            System.out.print(StringUtils.repeat(" ", leftPadding));
+            System.out.print(label);
+            System.out.print(StringUtils.repeat(" ", padding));
+            System.out.println(module.getDescription());
+        }
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        client = new KRAConnectorClient(parent.connection);
+
+        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);
+        }
+    }
+
+
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorRemoveCLI.java b/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorRemoveCLI.java
new file mode 100644
index 0000000000000000000000000000000000000000..d69e08ed4ee6a0f5c0e680d0033359d95c1590d0
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/system/KRAConnectorRemoveCLI.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 KRAConnectorRemoveCLI extends CLI {
+    public KRAConnectorCLI parent;
+
+    public KRAConnectorRemoveCLI(KRAConnectorCLI parent) {
+        super("del", "Remove KRA connector from CA");
+        this.parent = parent;
+    }
+
+    public void printHelp() {
+        formatter.printHelp(parent.name + "-" + name + " <KRA Host> <KRA Port>", options);
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        if (args.length != 2) {
+            printHelp();
+            System.exit(1);
+        }
+
+        String kraHost = args[0];
+        String kraPort = args[1];
+
+        parent.client.removeConnector(kraHost, kraPort);
+
+        MainCLI.printMessage("Removed KRA connector \""+kraHost + ":" + kraPort +"\"");
+    }
+}
diff --git a/base/native-tools/src/sslget/sslget.c b/base/native-tools/src/sslget/sslget.c
index 7288a1c58a0c2cc0c422cb51e3ce7a33de28bd21..a7c6a3f09d29b09cf8a25213e0bb9d821627ba1a 100644
--- a/base/native-tools/src/sslget/sslget.c
+++ b/base/native-tools/src/sslget/sslget.c
@@ -426,7 +426,11 @@ do_io( PRFileDesc *ssl_sock, int connection)
                  buf2 = 0;
              }
 
-             if ( ! PL_strnstr(buf,"200",13)) {
+             char *status_string = PL_strndup(buf+9, 3);
+             int status = atoi(status_string);
+             PL_strfree(status_string);
+
+             if (status >= 300) {
                  PR_Free(buf);
                  buf = 0;
                  exit(3);
-- 
1.7.11.7

