>From 5b3a7d2a32292994ef2c521314ddf41dc37525e7 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Wed, 27 Aug 2014 17:32:48 -0700
Subject: [PATCH] ticket 941 Renewal/Reenroll/Recovery/Renew policy 3rd cut;
 Renewal code; RetrieveCert code

---
 .../dogtagpki/server/connector/IRemoteRequest.java |   3 +
 .../server/tps/cms/CARemoteRequestHandler.java     |  81 ++++
 .../server/tps/cms/CARetrieveCertResponse.java     |  45 ++
 .../server/tps/cms/ConnectionManager.java          |   5 +-
 .../server/tps/config/ConnectorDatabase.java       |   1 +
 .../org/dogtagpki/server/tps/engine/TPSEngine.java |   4 +
 .../server/tps/processor/TPSEnrollProcessor.java   | 494 ++++++++++++++++++---
 .../server/tps/processor/TPSProcessor.java         |  15 +-
 8 files changed, 588 insertions(+), 60 deletions(-)
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java

diff --git a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
index 103aece028a6afc44a21caf54d6ec4e66d9dd5b9..24aa767d593ce6851d7d4c6edaa0133da9acfdcb 100644
--- a/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
+++ b/base/server/cms/src/org/dogtagpki/server/connector/IRemoteRequest.java
@@ -69,6 +69,8 @@ public interface IRemoteRequest {
     public static final String CA_RenewedCertificate = "renewedCertificate";
     public static final String CA_RENEWAL_SerialNum = "serial_num";
     public static final String CA_RENEWAL= "renewal";
+    public static final String CA_GET_CERT_B64CertOnly = "b64CertOnly";
+    public static final String CA_GET_CERT_SERIAL = "serialNumber";
 
     public static final String CA_REVOKE = "revoke";
     public static final String CA_REVOCATION_REASON = "revocationReason";
@@ -86,6 +88,7 @@ public interface IRemoteRequest {
     public static final String CA_RESPONSE_Certificate_b64 = "b64";
     public static final String CA_RESPONSE_Certificate_SubjectDN = "SubjectDN";
     public static final String CA_RESPONSE_Certificate_serial = "serialno";
+    public static final String CA_RESPONSE_Certificate_chain_b64 = "certChainBase64";
 
     // KRA request params
     public static final String KRA_UserId = "userid";
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java
index bc92363b3b3127d484bacb665c16e741bcfb40a2..fd0af7f2addc285ae85844aa9115a3d7c35c73fb 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARemoteRequestHandler.java
@@ -192,6 +192,87 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
     }
 
     /**
+     * retrieveCertificate retrieves a certificate by serial number
+     *
+     * @param serialno the serial number of the cert to be retrieved
+     * @return CARetrieveCertResponse
+     */
+    public CARetrieveCertResponse retrieveCertificate(
+            BigInteger serialno)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: renewCertificate(): begins.");
+        if (serialno == null) {
+            throw new EBaseException("CARemoteRequestHandler: retrieveCertificate(): input parameter null.");
+        }
+
+        IConfigStore conf = CMS.getConfigStore();
+        String configName = "tps.connector." + connid + ".uri.getBySerial";
+        String servlet = conf.getString(configName, "/ca/ee/ca/displayBySerial");
+        TPSSubsystem subsystem =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        HttpConnector conn =
+                (HttpConnector) subsystem.getConnectionManager().getConnector(connid);
+        CMS.debug("CARemoteRequestHandler: retrieveCertificate(): sending request to CA");
+        HttpResponse resp =
+                conn.send("getcert",
+                        IRemoteRequest.GET_XML + "=" + true +
+                                "&" + IRemoteRequest.CA_GET_CERT_B64CertOnly + "=" + true +
+                                "&" + IRemoteRequest.CA_GET_CERT_SERIAL + "=" + serialno.toString());
+
+        String content = resp.getContent();
+        if (content != null && !content.equals("")) {
+            XMLObject xmlResponse =
+                    getXMLparser(content);
+
+            Hashtable<String, Object> response =
+                    new Hashtable<String, Object>();
+
+            CMS.debug("CARemoteRequestHandler: retrieveCertificate(): received:" +
+                    content);
+
+            /**
+             * When a value is not found in response, keep going so we know
+             * what else is missing
+             */
+            Integer ist = new Integer(IRemoteRequest.RESPONSE_STATUS_NOT_FOUND);
+            String value = xmlResponse.getValue(IRemoteRequest.RESPONSE_STATUS_XML);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler: retrieveCertificate(): Status not found.");
+                CMS.debug("CARemoteRequestHandler: retrieveCertificate(): got content = " + content);
+            } else {
+                CMS.debug("CARemoteRequestHandler: retrieveCertificate(): got Status = " + value);
+                ist = Integer.parseInt(value);
+            }
+            response.put(IRemoteRequest.RESPONSE_STATUS, ist);
+
+            value = xmlResponse.getValue(IRemoteRequest.CA_RESPONSE_Certificate_chain_b64);
+            if (value == null) {
+                CMS.debug("CARemoteRequestHandler:: retrieveCertificate(): response missing name-value pair for: " +
+                        IRemoteRequest.CA_RESPONSE_Certificate_chain_b64);
+            } else {
+                CMS.debug("CARemoteRequestHandler:: retrieveCertificate(): got IRemoteRequest.CA_RESPONSE_Certificate_chain_b64 = "
+                        + value);
+                response.put(IRemoteRequest.CA_RESPONSE_Certificate_chain_b64, value);
+                try {
+                    X509CertImpl newCert = new X509CertImpl(Utils.base64decode(value));
+                    response.put(IRemoteRequest.CA_RESPONSE_Certificate_x509, newCert);
+                    CMS.debug("CARemoteRequestHandler: retrieveCertificate(): retrieved cert parsed successfully");
+                } catch (CertificateException e) {
+                    // we don't exit.  Keep going.
+                    CMS.debug("CARemoteRequestHandler: retrieveCertificate(): exception:" + e);
+                }
+            }
+
+            CMS.debug("CARemoteRequestHandler: retrieveCertificate(): ends.");
+            return new CARetrieveCertResponse(response);
+        } else {
+            CMS.debug("CARemoteRequestHandler: retrieveCertificate(): no response content");
+            throw new EBaseException("CARemoteRequestHandler: retrieveCertificate(): no response content.");
+        }
+    }
+
+    /**
      * renewCertificate renew a certificate by serial number
      *
      * @param serialno the serial number of the cert to be renewed
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..a356907facc6f6d23f6d4172e2319dbae6567755
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/CARetrieveCertResponse.java
@@ -0,0 +1,45 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2014 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.cms;
+
+import java.util.Hashtable;
+
+import netscape.security.x509.X509CertImpl;
+
+import org.dogtagpki.server.connector.IRemoteRequest;
+
+/**
+ * CARetrieveCertResponse is the class for the response to
+ * CA Remote Request: retrieveCertificate()
+ *
+ */
+public class CARetrieveCertResponse extends RemoteResponse
+{
+    public CARetrieveCertResponse(Hashtable<String, Object> ht) {
+        nameValTable = ht;
+    }
+
+    public String getCertB64() {
+        return (String) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_chain_b64);
+    }
+
+    public X509CertImpl getCert() {
+        return (X509CertImpl) nameValTable.get(IRemoteRequest.CA_RESPONSE_Certificate_x509);
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java
index 41f622a7da4b144a38bdc8b60c562693edbfc6dd..2b5ab220895ec991fe548e7136be26c01ac955a6 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/cms/ConnectionManager.java
@@ -19,9 +19,9 @@
 package org.dogtagpki.server.tps.cms;
 
 import java.util.Arrays;
-import java.util.List;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.List;
 
 import javax.ws.rs.core.MediaType;
 
@@ -84,10 +84,11 @@ public class ConnectionManager
      *   tps.connector.ca1.nickName=subsystemCert cert-pki-tomcat TPS
      *   tps.connector.ca1.timeout=30
      *   # In the example below,
-     *   #   "enrollment", "renewal", "revoke", and "unrevoke"
+     *   #   "enrollment", "getcert", "renewal", "revoke", and "unrevoke"
      *   #   are what's being referred to as "op" in the multi-uri support code
      *   tps.connector.ca1.uri.enrollment=/ca/ee/ca/profileSubmitSSLClient
      *   tps.connector.ca1.uri.renewal=/ca/ee/ca/profileSubmitSSLClient
+     *   tps.connector.ca1.uri.getcert=/ca/ee/ca/displayBySerial
      *   tps.connector.ca1.uri.revoke=/ca/ee/subsystem/ca/doRevoke
      *   tps.connector.ca1.uri.unrevoke=/ca/ee/subsystem/ca/doUnrevoke
      */
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java
index 313d8c901b6200165d0d4561faaefde4d630807f..4b1589797fcda602859d512e2e93003bdbcaa160 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConnectorDatabase.java
@@ -184,6 +184,7 @@ public class ConnectorDatabase extends CSCfgDatabase<ConnectorRecord> {
         record.setProperty(prefix + "." + id + ".nickName", nickname);
         record.setProperty(prefix + "." + id + ".timeout", "30");
         record.setProperty(prefix + "." + id + ".uri.enrollment", "/ca/ee/ca/profileSubmitSSLClient");
+        record.setProperty(prefix + "." + id + ".uri.getcert", "/ca/ee/ca/displayBySerial");
         record.setProperty(prefix + "." + id + ".uri.renewal", "/ca/ee/ca/profileSubmitSSLClient");
         record.setProperty(prefix + "." + id + ".uri.revoke", "/ca/ee/subsystem/ca/doRevoke");
         record.setProperty(prefix + "." + id + ".uri.unrevoke", "/ca/ee/subsystem/ca/doUnrevoke");
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/engine/TPSEngine.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/engine/TPSEngine.java
index f06f11f6967c5a1f5e6e1c4ecac6055a794c521b..c401ae07d23daafc1678567427a6c752c337f69b 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/engine/TPSEngine.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/engine/TPSEngine.java
@@ -122,6 +122,10 @@ public class TPSEngine {
     public static final String CFG_SERVER_KEY_ARCHIVAL = "serverKeygen.archive";
     public static final String CFG_DRM_CONNECTOR = "serverKeygen.drm.conn";
 
+    /* token renewal values */
+    public static final String CFG_RENEW_KEYTYPE_NUM = "renewal.keyType.num";
+    public static final String CFG_RENEW_KEYTYPE_VALUE = "renewal.keyType.value";
+
     /* External reg values */
 
     public static final String CFG_EXTERNAL_REG = "externalReg";
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
index ecd86bd1ad902c1d1cbe3115e292fc0a6e9bd761..0bcc42b2fdfae7c43b8a4e47535282e29410e7a8 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
@@ -1,9 +1,11 @@
 package org.dogtagpki.server.tps.processor;
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Random;
@@ -22,6 +24,8 @@ import org.dogtagpki.server.tps.channel.SecureChannel;
 import org.dogtagpki.server.tps.channel.SecureChannel.TokenKeyType;
 import org.dogtagpki.server.tps.cms.CAEnrollCertResponse;
 import org.dogtagpki.server.tps.cms.CARemoteRequestHandler;
+import org.dogtagpki.server.tps.cms.CARenewCertResponse;
+import org.dogtagpki.server.tps.cms.CARetrieveCertResponse;
 import org.dogtagpki.server.tps.cms.KRAServerSideKeyGenResponse;
 import org.dogtagpki.server.tps.dbs.ActivityDatabase;
 import org.dogtagpki.server.tps.dbs.TPSCertRecord;
@@ -221,7 +225,7 @@ public class TPSEnrollProcessor extends TPSProcessor {
 
         boolean renewed = false;
         TPSStatus status = generateCertsAfterRenewalRecoveryPolicy(certsInfo, channel, appletInfo);
-        //anything failed would have thrown an exception
+        //most failed would have thrown an exception
         String statusString = "Unknown"; // gives some meaningful debug message
         if (status == TPSStatus.STATUS_NO_ERROR)
             statusString = "Enrollment to follow";
@@ -229,6 +233,11 @@ public class TPSEnrollProcessor extends TPSProcessor {
             statusString = "Recovery processed";
         else if (status == TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED)
             statusString = "Renewal processed";
+        else {
+            auditMsg = " generateCertsAfterRenewalRecoveryPolicy returned status=" + status;
+            CMS.debug("TPSEnrollProcessor.enroll:" + auditMsg);
+            throw new TPSException(auditMsg);
+        }
         auditMsg = "generateCertsAfterRenewalRecoveryPolicy returns status:"
                 + EndOpMsg.statusToInt(status) + " : " + statusString;
         CMS.debug("TPSEnrollProcessor.enroll: " + auditMsg);
@@ -667,56 +676,21 @@ public class TPSEnrollProcessor extends TPSProcessor {
         CMS.debug(method + " found " + tokenRecords.size() + " tokens for user:" + userid);
         boolean isRecover = false;
 
+        TokenRecord lostToken = null;
         for (TokenRecord tokenRecord:tokenRecords)  {
             CMS.debug(method + " token id:"
                     + tokenRecord.getId() + "; status="
                     + tokenRecord.getStatus());
-            if (isRecover == true) { // this could be set in previous iteration
-                TokenRecord lostToken = tokenRecord;
-                String reasonStr = lostToken.getReason();
-                //RevocationReason reason = RevocationReason.valueOf(reasonStr);
-                String origTokenType = selectedTokenType;
-                auditMsg = "isRecover true; reasonStr =" + reasonStr;
-                CMS.debug(method + auditMsg);
 
-                if (reasonStr.equals("keyCompromise")) {
-                    return processRecovery();
-                } else if (reasonStr.equals("onHold")) {
-                    /*
-                     * the inactive one becomes the temp token
-                     * No recovery scheme, basically we are going to
-                     * do the brand new enrollment
-                     */
-                    IConfigStore configStore = CMS.getConfigStore();
-                    String configName = TPSEngine.OP_ENROLL_PREFIX + "." + getSelectedTokenType() + "temporaryToken.tokenType";
-                    try {
-                        String tmpTokenType = configStore.getString(configName);
-                    } catch (EPropertyNotFound e) {
-                        auditMsg = "configuration " + configName + " not found";
-                        CMS.debug(method + auditMsg);
-                        throw new TPSException(method + auditMsg);
-                    } catch (EBaseException e) {
-                        auditMsg = "configuration " + configName + " not found";
-                        CMS.debug(method + auditMsg);
-                        throw new TPSException(method + auditMsg);
-                    }
-                    return processRecovery();
-
-                } else if (reasonStr.equals("destroyed")) {
-                    return processRecovery();
-                } else {
-                    auditMsg = "No such lost reason: " + reasonStr + " for this cuid: " + aInfo.getCUIDhexStringPlain();
-                    CMS.debug(method + auditMsg);
-                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_LOST_REASON);
-                }
-
-            }
-
-            //Is this the same token?
+            //Is this the same token (current token)?
             if (tokenRecord.getId().equals(aInfo.getCUIDhexStringPlain())) {
                 //same token
+                auditMsg = "found current token entry";
+                CMS.debug(method + ":" + auditMsg);
                 if (tokenRecord.getStatus().equals("uninitialized")) {
+                    // this is the current token
                     if (tokenRecords.size() == 1) {
+                        // the current token is the only token owned by the user
                         CMS.debug(method +  ": need to do enrollment");
                         // need to do enrollment outside
                         break;
@@ -724,28 +698,35 @@ public class TPSEnrollProcessor extends TPSProcessor {
                         CMS.debug(method +  ": There are multiple token entries for user "
                                 + userid);
                         try {
+                            // this is assuming that the user can only have one single active token
+                            // TODO: for future, maybe should allow multiple active tokens
                             tps.tdb.tdbHasActiveToken(userid);
-                            auditMsg = method + ": user already has an active token";
-                            CMS.debug(auditMsg);
-                            throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_HAS_AT_LEAST_ONE_ACTIVE_TOKEN);
-                        } catch (Exception e1) {// user has no active token
+
+                        } catch (Exception e1) {
                                 /*
-                                 * current token is in active state
+                                 * user has no active token, need to find a token to recover from
                                  * there are no other active tokens for this user
-                                 * that means the previous one is the lost one
-                                 *
-                                 * get the most recent previous token:
                                  */
                                 isRecover = true;
+                                continue; // TODO: or break?
                         }
+                        auditMsg = method + ": user already has an active token";
+                        CMS.debug(auditMsg);
+                        throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_HAS_AT_LEAST_ONE_ACTIVE_TOKEN);
                     }
                 } else if (tokenRecord.getStatus().equals("active")) {
+                    // current token is already active; renew if allowed
                     if (tokenPolicy.isAllowdTokenRenew(aInfo.getCUIDhexStringPlain())) {
-                        return processRenewal();                    }
+                        return processRenewal(certsInfo, channel, aInfo, tokenRecord);
+                    } else {
+                        auditMsg = "token is already active; can't renew because renewal is not allowed; will re-enroll if allowed";
+                        CMS.debug(method + ":" + auditMsg);
+                    }
                     break;
                 } else if (tokenRecord.getStatus().equals("terminated")) {
-                    auditMsg = method + ": terminated token cuid="
-                            + aInfo.getCUIDhexStringPlain();
+                    auditMsg = "terminated token cuid="
+                            + aInfo.getCUIDhexStringPlain() + " cannot be reused";
+                    CMS.debug(method + ":" + auditMsg);
                     throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_CONTACT_ADMIN);
                 } else if (tokenRecord.getStatus().equals("lost")) {
                     String reasonStr = tokenRecord.getReason();
@@ -784,23 +765,420 @@ public class TPSEnrollProcessor extends TPSProcessor {
                     throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_TOKEN_STATE);
                 }
             } else { //cuid != current token
+                auditMsg = "found token entry different from current token";
+                CMS.debug(method + ":" + auditMsg);
+                if (tokenRecord.getStatus().equals("lost")) {
+                    //lostostToken keeps track of the latest token that's lost
+                    //last one in the look should be the latest
+                    lostToken = tokenRecord;
+                    auditMsg = "found a lost token: cuid = " + tokenRecord.getId();
+                    CMS.debug(method + ":" + auditMsg);
+                }
                 continue;
             }
         }
 
+        if (isRecover == true) { // this could be set in previous iteration
+            if (lostToken == null) {
+                auditMsg = "No lost token to be recovered; do enrollment";
+                CMS.debug(method + ":" + auditMsg);
+                //shouldn't even get here;  But if we do, just enroll
+            } else {
+                String reasonStr = lostToken.getReason();
+                //RevocationReason reason = RevocationReason.valueOf(reasonStr);
+                String origTokenType = selectedTokenType;
+                auditMsg = "isRecover true; reasonStr =" + reasonStr;
+                CMS.debug(method + ":" + auditMsg);
+
+                if (reasonStr.equals("keyCompromise")) {
+                    return processRecovery();
+                } else if (reasonStr.equals("onHold")) {
+                    /*
+                     * the inactive one becomes the temp token
+                     * No recovery scheme, basically we are going to
+                     * do the brand new enrollment
+                     */
+                    IConfigStore configStore = CMS.getConfigStore();
+                    String configName = TPSEngine.OP_ENROLL_PREFIX + "." + getSelectedTokenType()
+                            + ".temporaryToken.tokenType";
+                    try {
+                        String tmpTokenType = configStore.getString(configName);
+                    } catch (EPropertyNotFound e) {
+                        auditMsg = "configuration " + configName + " not found";
+                        CMS.debug(method + ":" + auditMsg);
+                        throw new TPSException(method + auditMsg);
+                    } catch (EBaseException e) {
+                        auditMsg = "configuration " + configName + " not found";
+                        CMS.debug(method + auditMsg);
+                        throw new TPSException(method + auditMsg);
+                    }
+                    return processRecovery();
+
+                } else if (reasonStr.equals("destroyed")) {
+                    return processRecovery();
+                } else {
+                    auditMsg = "No such lost reason: " + reasonStr + " for this cuid: " + aInfo.getCUIDhexStringPlain();
+                    CMS.debug(method + ":" + auditMsg);
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_LOST_REASON);
+                }
+            }
+        }
+
         CMS.debug(method + ": ends");
         return status;
     }
 
-    private TPSStatus processRenewal() {
-        TPSStatus status = TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED;
+    /*
+    * Renewal logic
+    *  1. Create Optional local TPS grace period per token profile,
+    *     per token type, such as signing or encryption.
+    *    This grace period must match how the CA is configured. Ex:
+    *    op.enroll.userKey.renewal.encryption.enable=true
+    *    op.enroll.userKey.renewal.encryption.gracePeriod.enable=true
+    *    op.enroll.userKey.renewal.encryption.gracePeriod.before=30
+    *    op.enroll.userKey.renewal.encryption.gracePeriod.after=30
+    *  2. In case of a grace period failure the code will go on
+    *     and attempt to renew the next certificate in the list.
+    *  3. In case of any other code failure, the code will abort
+    *     and leave the token untouched, while informing the user
+    *     with an error message.
+    *
+    */
+    private TPSStatus processRenewal(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo, TokenRecord tokenRecord)
+    throws TPSException {
+        TPSStatus status = TPSStatus.STATUS_ERROR_RENEWAL_FAILED;
+        String method = "TPSEnrollProcess.processRenewal";
+        String auditMsg;
+        CMS.debug(method + ": begins");
 
-        // TODO Auto-generated method stub
-        CMS.debug("TPSEnrollProcess.processRenewal: reached");
+        boolean noFailedCerts = true;
 
+        if (certsInfo == null || aInfo == null || channel == null) {
+            throw new TPSException(method + ": Bad Input data!",
+                    TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
+        }
+
+        TPSSubsystem tps =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        int keyTypeNum = getNumberCertsToRenew();
+        /*
+         * Get certs from the tokendb for this token to find out about
+         * renewal possibility
+         */
+        ArrayList<TPSCertRecord> allCerts = tps.tdb.tdbGetCertificatesByCUID(tokenRecord.getId());
+
+        certsInfo.setNumCertsToEnroll(keyTypeNum);
+
+        CMS.debug(method + ": Number of certs to renew: " + keyTypeNum);
+
+        for (int i = 0; i < keyTypeNum; i++) {
+            /*
+             * e.g. op.enroll.userKey.renewal.keyType.value.0=signing
+             * e.g. op.enroll.userKey.renewal.keyType.value.1=encryption
+             */
+            String keyType = getRenewConfigKeyType(i);
+            boolean renewEnabled = getRenewEnabled(keyType);
+            CMS.debug(method + ": key type " + keyType);
+            if (!renewEnabled) {
+                CMS.debug(method + ": renew not enabled");
+                continue;
+            }
+
+            CMS.debug(method + ": renew enabled");
+
+            TokenKeyType keyTypeEnum;
+            if (keyType.equalsIgnoreCase("signing"))
+                keyTypeEnum = TokenKeyType.KEY_TYPE_SIGNING;
+            else if (keyType.equalsIgnoreCase("encryption"))
+                keyTypeEnum = TokenKeyType.KEY_TYPE_ENCRYPTION;
+            else
+                keyTypeEnum = TokenKeyType.KEY_TYPE_SIGNING_AND_ENCRYPTION;
+
+            //TODO: handle the $token$ config pattern for label?
+
+            certsInfo.setCurrentCertIndex(i);
+
+            CertEnrollInfo cEnrollInfo = new CertEnrollInfo();
+            IConfigStore configStore = CMS.getConfigStore();
+
+            // find all config
+            String configName = null;
+            boolean graceEnabled = false;
+            String graceBeforeS = null;
+            String graceAfterS = null;
+            try {
+                String keyTypePrefix = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + ".renewal." + keyType;
+
+                //TODO: profileId is actually gotten in the CARemoteRequestHandler.
+                configName = keyTypePrefix + ".ca.profileId";
+                String profileId;
+                profileId = configStore.getString(configName);
+                CMS.debug(method + ": profileId: " + profileId);
+
+                configName = keyTypePrefix + ".gracePeriod.enable";
+                graceEnabled = configStore.getBoolean(configName, false);
+                if (graceEnabled) {
+                    CMS.debug(method + ": grace period check is enabled");
+                    configName = keyTypePrefix + ".gracePeriod.before";
+                    graceBeforeS = configStore.getString(configName, "");
+                    configName = keyTypePrefix + ".gracePeriod.after";
+                    graceAfterS = configStore.getString(configName, "");
+                } else {
+                    CMS.debug(method + ": grace period check is not enabled");
+                }
+
+                configName = keyTypePrefix + ".certId";
+                String certId = configStore.getString(configName, "C0");
+                CMS.debug(method + ": certId: " + certId);
+
+                configName = keyTypePrefix + ".certAttrId";
+                String certAttrId = configStore.getString(configName, "c0");
+                CMS.debug(method + ": certAttrId: " + certAttrId);
+
+                configName = keyTypePrefix + ".privateKeyAttrId";
+                String priKeyAttrId = configStore.getString(configName, "k0");
+                CMS.debug(method + ": privateKeyAttrId: " + priKeyAttrId);
+
+                configName = keyTypePrefix + ".publicKeyAttrId";
+                String publicKeyAttrId = configStore.getString(configName, "k1");
+                CMS.debug(method + ": publicKeyAttrId: " + publicKeyAttrId);
+
+            } catch (EBaseException e) {
+                throw new TPSException(method + ": Internal error finding config value: " + configName + ":"
+                        + e,
+                        TPSStatus.STATUS_ERROR_MISCONFIGURATION);
+            }
+
+            // find the certs that match the keyType to renew
+            for (TPSCertRecord cert: allCerts) {
+                if (keyType.equals(cert.getKeyType())) {
+                    try {
+                        CMS.debug(method + ": cert " + cert.getId() + " with status:" + cert.getStatus());
+                        if (cert.getStatus().equals("revoked") ||
+                                cert.getStatus().equals("renewed")) {
+                            CMS.debug(method + ": cert status is not to be renewed");
+                            continue;
+                        }
+
+                        // check if within grace period to save us a trip (note: CA makes the final decision)
+                        if (graceEnabled) {
+                            try {
+                                if (!isCertWithinRenewalGracePeriod(cert, graceBeforeS, graceAfterS))
+                                    continue;
+                            } catch (TPSException ge) {
+                                // error in this will just log and keep going
+                                CMS.debug(method + ":" + ge + "; continue to try renewal");
+                            }
+                        }
+                        renewCertificate(cert, certsInfo, channel, aInfo, keyType);
+                        status = TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED;
+                    } catch (TPSException e) {
+                        CMS.debug(method + "renewCertificate: exception:" + e);
+                        noFailedCerts = false;
+                        break; //need to clean up half-done token later
+                    }
+                }
+            }
+        }
+
+        if (!noFailedCerts) {
+            // TODO: handle cleanup
+            auditMsg = "There has been failed cert renewal";
+            CMS.debug(method + ":" + auditMsg);
+            throw new TPSException(auditMsg + TPSStatus.STATUS_ERROR_RENEWAL_FAILED);
+        }
         return status;
     }
 
+    /*
+     * isCertWithinRenewalGracePeriod - check if a cert is within the renewal grace period
+     * @param cert the cert to be renewed
+     * @param renewGraceBeforeS string representation of the # of days "before" cert expiration date
+     * @param renewGraceAfterS string representation of the # of days "after" cert expiration date
+     */
+    private boolean isCertWithinRenewalGracePeriod(TPSCertRecord cert, String renewGraceBeforeS, String renewGraceAfterS) throws TPSException {
+        String method = "TPSEnrollProcessor.isCertWithinRenewalGracePeriod";
+        int renewGraceBefore = 0;
+        int renewGraceAfter = 0;
+
+        if (cert == null || renewGraceBeforeS == null || renewGraceAfterS == null) {
+            CMS.debug(method + ": missing some input");
+            throw new TPSException(method + ": Bad Input data!",
+                    TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
+        }
+        BigInteger renewGraceBeforeBI = new BigInteger(renewGraceBeforeS);
+        BigInteger renewGraceAfterBI = new BigInteger(renewGraceAfterS);
+
+        // -1 means no limit
+        if (renewGraceBeforeS == "")
+            renewGraceBefore = -1;
+        else
+            renewGraceBefore = Integer.parseInt(renewGraceBeforeS);
+
+        if (renewGraceAfterS == "")
+            renewGraceAfter = -1;
+        else
+            renewGraceAfter = Integer.parseInt(renewGraceAfterS);
+
+        if (renewGraceBefore > 0)
+            renewGraceBeforeBI = renewGraceBeforeBI.multiply(BigInteger.valueOf(1000 * 86400));
+        if (renewGraceAfter > 0)
+            renewGraceAfterBI = renewGraceAfterBI.multiply(BigInteger.valueOf(1000 * 86400));
+
+        Date origExpDate = cert.getValidNotAfter();
+        Date current = CMS.getCurrentDate();
+        long millisDiff = origExpDate.getTime() - current.getTime();
+        CMS.debug(method + ": millisDiff="
+                + millisDiff + " origExpDate=" + origExpDate.getTime() + " current=" + current.getTime());
+
+        /*
+         * "days", if positive, has to be less than renew_grace_before
+         * "days", if negative, means already past expiration date,
+         *     (abs value) has to be less than renew_grace_after
+         * if renew_grace_before or renew_grace_after are negative
+         *    the one with negative value is ignored
+         */
+        if (millisDiff >= 0) {
+            if ((renewGraceBefore > 0) && (millisDiff > renewGraceBeforeBI.longValue())) {
+                CMS.debug(method + ": renewal attempted outside of grace period;" +
+                        renewGraceBefore + " days before and " +
+                                renewGraceAfter + " days after original cert expiration date");
+                return false;
+            }
+        } else {
+            if ((renewGraceAfter > 0) && ((0 - millisDiff) > renewGraceAfterBI.longValue())) {
+                CMS.debug(method + ": renewal attempted outside of grace period;" +
+                        renewGraceBefore + " days before and " +
+                                renewGraceAfter + " days after original cert expiration date");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /*
+     * renewCertificate - renew by serial number
+     */
+    private void renewCertificate(TPSCertRecord cert, EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo, String keyType)
+        throws TPSException {
+        String method = "TPSEnrollProcessor.renewCertificate";
+        CMS.debug(method + " begins");
+
+        if (cert == null || certsInfo == null || aInfo == null || channel == null || aInfo == null || keyType == null) {
+            CMS.debug(method + ": missing some input");
+            throw new TPSException(method + ": Bad Input data!",
+                    TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
+        }
+        String serialS = cert.getSerialNumber();
+        CMS.debug(method + ": serial # =" + serialS);
+        String serialhex = serialS.substring(2); // strip off the "0x"
+        BigInteger serialBI = new BigInteger(serialhex, 16);
+
+        try {
+            String caConnID = getCAConnectorID();
+            CARemoteRequestHandler caRH = new CARemoteRequestHandler(caConnID);
+
+            /*
+             * testing retrieveCertificate() to retrieve the old cert (to be used by Recovery)
+             * TODO: remove
+             */
+            CARetrieveCertResponse retrieveResponse = caRH.retrieveCertificate(serialBI);
+            String retrievedCertB64 = retrieveResponse.getCertB64();
+            CMS.debug(method + ": retrieved cert: " + retrievedCertB64);
+            // test ends - remove up to here
+
+            CARenewCertResponse renewResponse = caRH.renewCertificate(serialBI, cert.getType(), keyType);
+            String newCertB64 = renewResponse.getRenewedCertB64();
+            CMS.debug(method + ": renewed new cert: " + newCertB64);
+            /*
+             * TODO: fill up certsInfo and do the cert injection
+             */
+        } catch (EBaseException e) {
+            CMS.debug(method + ":" + e);
+            throw new TPSException(method + ": Exception thrown: " + e,
+                    TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
+        }
+
+        return;
+    }
+
+    private boolean getRenewEnabled(String keyType) {
+        String method = "TPSEnrollProcessor.getRenewEnabled";
+        IConfigStore configStore = CMS.getConfigStore();
+        boolean enabled = false;
+
+        try {
+            String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + ".renewal."
+                    + keyType + "." + "enable";
+            enabled = configStore.getBoolean(
+                    configValue, false);
+
+        } catch (EBaseException e) {
+            //default to false
+        }
+
+        CMS.debug(method + ": returning " + enabled);
+        return enabled;
+    }
+
+    private String getRenewConfigKeyType(int keyTypeIndex) throws TPSException {
+        String method = "TPSEnrollProcessor.getRenewConfigKeyType";
+        IConfigStore configStore = CMS.getConfigStore();
+        String keyType = null;
+
+        try {
+            String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "."
+                    + TPSEngine.CFG_RENEW_KEYTYPE_VALUE + "." + keyTypeIndex;
+            keyType = configStore.getString(
+                    configValue, null);
+
+        } catch (EBaseException e) {
+            throw new TPSException(
+                    method + ": Internal error finding config value: " + e,
+                    TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
+        }
+
+        //We would really like one of these to exist
+        if (keyType == null) {
+            throw new TPSException(
+                    method + ": Internal error finding config value: ",
+                    TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
+        }
+
+        CMS.debug(method + ": returning: " + keyType);
+
+        return keyType;
+
+    }
+
+
+    private int getNumberCertsToRenew() throws TPSException {
+        String method = "TPSEnrollProcessor.getNumberCertsToRenew";
+
+        IConfigStore configStore = CMS.getConfigStore();
+        int keyTypeNum = 0;
+        try {
+            String configValue = TPSEngine.OP_ENROLL_PREFIX + "." + selectedTokenType + "."
+                    + TPSEngine.CFG_RENEW_KEYTYPE_NUM;
+            keyTypeNum = configStore.getInteger(
+                    configValue, 0);
+
+        } catch (EBaseException e) {
+            throw new TPSException(method + ": Internal error finding config value: "
+                    + e,
+                    TPSStatus.STATUS_ERROR_MISCONFIGURATION);
+        }
+
+        if (keyTypeNum == 0) {
+            throw new TPSException(
+                    method + ": invalid number of certificates to renew configured!",
+                    TPSStatus.STATUS_ERROR_MISCONFIGURATION);
+        }
+        CMS.debug(method + ": returning: " + keyTypeNum);
+
+        return keyTypeNum;
+    }
+
     private TPSStatus processRecovery() {
         TPSStatus status = TPSStatus.STATUS_ERROR_RECOVERY_IS_PROCESSED;
 
@@ -1177,6 +1555,8 @@ public class TPSEnrollProcessor extends TPSProcessor {
             certsInfo.addKType(cEnrollInfo.getKeyType());
             certsInfo.addOrigin(aInfo.getCUIDhexString());
 
+            certsInfo.addTokenType(selectedTokenType);
+
             SubjectPublicKeyInfo publicKeyInfo = null;
             try {
                 if (serverSideKeyGen) {
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSProcessor.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
index 85ad5ff852031939b5d8b17ac8f7fd04d4ab8669..e66f3023e538c1f7fd99830cc5f0a3352d5e580c 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/TPSProcessor.java
@@ -1163,7 +1163,20 @@ public class TPSProcessor {
             throw e;
         }
         appletInfo.setAid(getCardManagerAID());
+
+        CMS.debug("TPSProcessor.format: token cuid: " + appletInfo.getCUIDhexStringPlain());
         boolean isTokenPresent = false;
+        try {
+            tokenRecord = tps.tdb.tdbGetTokenEntry(appletInfo.getCUIDhexStringPlain());
+            // now the in memory tokenRecord is replaced by the actual token data
+            CMS.debug("TPSProcessor.format: found token...");
+            isTokenPresent = true;
+        } catch (Exception e) {
+            CMS.debug("TPSProcessor.format: token does not exist in tokendb... create one in memory");
+            tokenRecord = new TokenRecord();
+            tokenRecord.setId(appletInfo.getCUIDhexStringPlain());
+        }
+
         fillTokenRecord(tokenRecord, appletInfo);
         session.setTokenRecord(tokenRecord);
 
@@ -1286,7 +1299,6 @@ public class TPSProcessor {
             }
         } else {
             CMS.debug("TPSProcessor.format: token does not exist");
-            tokenRecord.setStatus("uninitialized");
 
             checkAllowUnknownToken(TPSEngine.OP_FORMAT_PREFIX);
         }
@@ -1339,6 +1351,7 @@ public class TPSProcessor {
         }
 
         // Update Token DB
+       tokenRecord.setStatus("uninitialized");
         try {
             tps.tdb.tdbUpdateTokenEntry(tokenRecord);
             String successMsg = "update token success";
-- 
1.8.4.2

