>From b206c385b3497395b06c57b79af79114e5ebe6dc Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Sat, 16 Aug 2014 13:46:20 -0700
Subject: [PATCH] ticket#882 tokendb policy handling, revocation and re-enroll

---
 base/common/src/org/dogtagpki/tps/main/Util.java   |  44 +++
 base/tps-tomcat/shared/conf/CS.cfg.in              |   3 +-
 .../src/org/dogtagpki/server/tps/TPSSubsystem.java |   2 +-
 .../org/dogtagpki/server/tps/TPSTokenPolicy.java   | 158 +++++++++
 .../src/org/dogtagpki/server/tps/TPSTokendb.java   | 137 ++++++--
 .../server/tps/cms/CARemoteRequestHandler.java     | 111 +++---
 .../dogtagpki/server/tps/dbs/TPSCertRecord.java    |   3 +
 .../server/tps/processor/EnrolledCertsInfo.java    | 112 +++++++
 .../server/tps/processor/TPSEnrollProcessor.java   | 373 +++++++++++++++++----
 .../server/tps/processor/TPSProcessor.java         | 293 +++++++++++++---
 .../dogtagpki/server/tps/rest/TokenService.java    |  63 ++--
 11 files changed, 1081 insertions(+), 218 deletions(-)
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokenPolicy.java

diff --git a/base/common/src/org/dogtagpki/tps/main/Util.java b/base/common/src/org/dogtagpki/tps/main/Util.java
index 8e6ffe61480c66bdf8777db8902b3627547712a6..c39b43577bef22362688a94e7f77703cfc3ad49e 100644
--- a/base/common/src/org/dogtagpki/tps/main/Util.java
+++ b/base/common/src/org/dogtagpki/tps/main/Util.java
@@ -20,11 +20,18 @@
  */
 package org.dogtagpki.tps.main;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.security.spec.AlgorithmParameterSpec;
 
+import netscape.security.x509.AuthorityKeyIdentifierExtension;
+import netscape.security.x509.KeyIdentifier;
+import netscape.security.x509.PKIXExtensions;
+import netscape.security.x509.SubjectKeyIdentifierExtension;
+import netscape.security.x509.X509CertImpl;
+
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.Cipher;
 import org.mozilla.jss.crypto.CryptoToken;
@@ -32,6 +39,7 @@ import org.mozilla.jss.crypto.EncryptionAlgorithm;
 import org.mozilla.jss.crypto.IVParameterSpec;
 import org.mozilla.jss.pkcs11.PK11SymKey;
 
+import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.cmsutil.util.Utils;
 
@@ -288,4 +296,40 @@ public class Util {
 
     }
 
+    /*
+     * getCertAkiString returns the Authority Key Identifier of the certificate in Base64 encoding
+     * @param cert X509CertImpl of the cert to be processed
+     * @return Base64 encoding of the cert's AKI
+     */
+    public static String getCertAkiString(X509CertImpl cert)
+            throws EBaseException, IOException {
+        if (cert == null) {
+            throw new EBaseException("CARemoteRequestHandler: getCertAkiString(): input parameter cert null.");
+        }
+        AuthorityKeyIdentifierExtension certAKI =
+                (AuthorityKeyIdentifierExtension)
+                cert.getExtension(PKIXExtensions.AuthorityKey_Id.toString());
+        KeyIdentifier kid =
+                (KeyIdentifier) certAKI.get(AuthorityKeyIdentifierExtension.KEY_ID);
+        return (CMS.BtoA(kid.getIdentifier()).trim());
+    }
+
+    /*
+     * getCertAkiString returns the Subject Key Identifier of the certificate in Base64 encoding
+     * @param cert X509CertImpl of the cert to be processed
+     * @return Base64 encoding of the cert's SKI
+     */
+    public static String getCertSkiString(X509CertImpl cert)
+            throws EBaseException, IOException {
+        if (cert == null) {
+            throw new EBaseException("CARemoteRequestHandler: getCertSkiString(): input parameter cert null.");
+        }
+        SubjectKeyIdentifierExtension certSKI =
+                (SubjectKeyIdentifierExtension)
+                cert.getExtension(PKIXExtensions.SubjectKey_Id.toString());
+        KeyIdentifier kid =
+                (KeyIdentifier) certSKI.get(SubjectKeyIdentifierExtension.KEY_ID);
+        return (CMS.BtoA(kid.getIdentifier()).trim());
+    }
+
 }
diff --git a/base/tps-tomcat/shared/conf/CS.cfg.in b/base/tps-tomcat/shared/conf/CS.cfg.in
index 4ae2b222f1ac528a8831b4aa22e7269fd4578c2f..e91b3451c2716a97d311de1b251b657d2b4ddfdf 100644
--- a/base/tps-tomcat/shared/conf/CS.cfg.in
+++ b/base/tps-tomcat/shared/conf/CS.cfg.in
@@ -35,6 +35,7 @@ auths.impl.UidPwdPinDirAuth.class=com.netscape.cms.authentication.UidPwdPinDirAu
 auths.instance.AgentCertAuth.agentGroup=Certificate Manager Agents
 auths.instance.AgentCertAuth.pluginName=AgentCertAuth
 auths.instance.TokenAuth.pluginName=TokenAuth
+auths.instance.ldap1.authCredName=uid
 auths.instance.ldap1.ui.retries=3
 auths.instance.ldap1.ui.title.en=LDAP Authentication
 auths.instance.ldap1.ui.description.en=This authenticates user against the LDAP directory.
@@ -1302,7 +1303,7 @@ tokendb.bindPassPath=[PKI_INSTANCE_PATH]/conf/password.conf
 tokendb.certBaseDN=ou=Certificates,[TOKENDB_ROOT]
 tokendb.confirmConfigChangesTemplate=confirmConfigChanges.template
 tokendb.confirmDeleteConfigTemplate=confirmDeleteConfig.template
-tokendb.defaultPolicy=RE_ENROLL=YES
+tokendb.defaultPolicy=RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO
 tokendb.deleteResultTemplate=deleteResults.template
 tokendb.deleteTemplate=delete.template
 tokendb.doTokenConfirmTemplate=doTokenConfirm.template
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java
index 0d5fe4bc4a1a61cff51d5ea3933443f3e0bf3fa1..75cdddadbcd0d02d80ade8a9f61b15ad251f42af 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java
@@ -105,7 +105,7 @@ public class TPSSubsystem implements IAuthority, ISubsystem {
         connectorDatabase = new ConnectorDatabase();
         profileDatabase = new ProfileDatabase();
         profileMappingDatabase = new ProfileMappingDatabase();
-        tdb = new TPSTokendb();
+        tdb = new TPSTokendb(this);
 
         engine = new TPSEngine();
         engine.init();
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokenPolicy.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokenPolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a866f73775e654208d6093615310deef7b171fc
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokenPolicy.java
@@ -0,0 +1,158 @@
+// --- 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;
+
+import org.dogtagpki.server.tps.dbs.TokenRecord;
+import org.dogtagpki.tps.main.TPSException;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.EPropertyNotFound;
+import com.netscape.certsrv.base.IConfigStore;
+
+/*
+ * TPSTokenPolicy - handles token enrollment related policies
+ *
+ * @author cfu
+ */
+public class TPSTokenPolicy {
+    private TPSSubsystem tps;
+    private static final String DEFAULT_POLICY_SET_STRING =
+            "RE_ENROLL=YES;RENEW=NO;FORCE_FORMAT=NO;PIN_RESET=NO;RESET_PIN_RESET_TO_NO=NO";
+    private boolean re_enroll = true;
+    private boolean renew = false;
+    private boolean force_format = false;
+    private boolean pin_reset = true;
+    private boolean reset_pin_reset_to_no = false;
+
+    public TPSTokenPolicy (TPSSubsystem tps) throws TPSException {
+        if (tps == null) {
+            String msg = "TPSTokenPolicy.TPSTokenPolicy: tps cannnot be null";
+            CMS.debug(msg);
+            throw new TPSException(msg);
+        }
+        this.tps = tps;
+        // init from config first
+        String policySetString = getDefaultPolicySetString();
+        parsePolicySetString(policySetString);
+
+    }
+
+    public String getDefaultPolicySetString() {
+        IConfigStore configStore = CMS.getConfigStore();
+        String configName = "tokendb.defaultPolicy";
+        String policySetString;
+        try {
+            policySetString = configStore.getString(configName);
+        } catch (EPropertyNotFound e) {
+            policySetString = DEFAULT_POLICY_SET_STRING;
+        } catch (EBaseException e) {
+            policySetString = DEFAULT_POLICY_SET_STRING;
+        }
+
+        return policySetString;
+    }
+
+    public void parsePolicySetString (String policySetString) {
+        if (policySetString == null)
+            return; // take the default
+
+        String[] policySet = policySetString.split(";");
+        for (String policyString : policySet) {
+            String[] policy = policyString.split("=");
+            if (policy[0].equalsIgnoreCase("RE_ENROLL"))
+                re_enroll = getBool(policy[1], true);
+            else if (policy[0].equalsIgnoreCase("RENEW"))
+                renew = getBool(policy[1], false);
+            else if (policy[0].equalsIgnoreCase("FORCE_FORMAT"))
+                force_format = getBool(policy[1], false);
+            else if (policy[0].equalsIgnoreCase("PIN_RESET"))
+                pin_reset = getBool(policy[1], false);
+            else if (policy[0].equalsIgnoreCase("RESET_PIN_RESET_TO_NO"))
+                reset_pin_reset_to_no = getBool(policy[1], false);
+            //else no change, just take the default;
+        }
+    }
+
+/*
+ * getBool translates string to boolean:
+ * true: "YES", "yes", "TRUE", "true"
+ * false: "NO", "no", "FALSE", "false"
+ *
+ * if tring is null or Anything othrer than the above, defaultbool is returned
+ */
+    private boolean getBool(String string, boolean defaultBool) {
+        if (string == null)
+            return defaultBool;
+
+        if (string.equalsIgnoreCase("YES") ||
+                string.equalsIgnoreCase("true")) {
+            return true;
+        } else if (string.equalsIgnoreCase("NO") ||
+                string.equalsIgnoreCase("false")) {
+            return false;
+        }
+
+        return defaultBool;
+    }
+
+    private void getUpdatedPolicy(String cuid) {
+        // note: default policy already initialized in the constructor
+        TokenRecord tokenRecord = null;
+        String policySetString = null;
+        try {
+            tokenRecord = tps.tdb.tdbGetTokenEntry(cuid);
+        } catch (Exception e) {
+            // just take the default;
+            return;
+        }
+
+        policySetString = tokenRecord.getPolicy();
+        parsePolicySetString(policySetString);
+    }
+
+    public boolean isAllowedTokenPinReset(String cuid) {
+        getUpdatedPolicy(cuid);
+
+        return reset_pin_reset_to_no;
+    }
+
+    public boolean isAllowedPinReset(String cuid) {
+        getUpdatedPolicy(cuid);
+
+        return pin_reset;
+    }
+
+    public boolean isForceTokenFormat(String cuid) {
+        getUpdatedPolicy(cuid);
+
+        return force_format;
+    }
+
+    public boolean isAllowdTokenReenroll(String cuid) {
+        getUpdatedPolicy(cuid);
+
+        return re_enroll;
+    }
+
+    public boolean isAllowdTokenRenew(String cuid) {
+        getUpdatedPolicy(cuid);
+
+        return renew;
+    }
+}
\ No newline at end of file
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokendb.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokendb.java
index 5b78c9dae8d99c78a4ec0f98e32f051566c4a190..eb7f02ac97ea8f578c77ada1f47451c107bae165 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokendb.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSTokendb.java
@@ -20,10 +20,14 @@ package org.dogtagpki.server.tps;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 
+import org.dogtagpki.server.tps.dbs.TPSCertRecord;
 import org.dogtagpki.server.tps.dbs.TokenRecord;
-import org.dogtagpki.server.tps.processor.EnrolledCertsInfo;
+import org.dogtagpki.tps.main.TPSException;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
@@ -34,9 +38,16 @@ import com.netscape.certsrv.tps.token.TokenStatus;
  * TPSTokendb class offers a collection of tokendb management convenience routines
  */
 public class TPSTokendb {
+    private TPSSubsystem tps;
     private Map<TokenStatus, Collection<TokenStatus>> allowedTransitions = new HashMap<TokenStatus, Collection<TokenStatus>>();
 
-    public TPSTokendb() throws EBaseException {
+    public TPSTokendb(TPSSubsystem tps) throws EBaseException {
+        if (tps == null) {
+            String msg = "TPStokendb.TPSTokendb: tps cannot be null";
+            CMS.debug(msg);
+            throw new EBaseException(msg);
+        }
+        this.tps = tps;
         try {
             initAllowedTransitions();
         } catch (Exception e) {
@@ -89,9 +100,9 @@ public class TPSTokendb {
      * tdbActivity logs token activities; This version is called by non-administrative functions
      */
     public void tdbActivity(
-            TPSSubsystem tpsSubsystem, String op, TokenRecord tokenRecord, String ip, String msg, String result) {
+            String op, TokenRecord tokenRecord, String ip, String msg, String result) {
         try {
-            tpsSubsystem.activityDatabase.log(
+            tps.activityDatabase.log(
                     ip,
                     (tokenRecord != null)? tokenRecord.getId():null,
                     op,
@@ -108,9 +119,9 @@ public class TPSTokendb {
      * tdbActivity logs token activities; This version is called by administrative functions
      */
     public void tdbActivity(
-            TPSSubsystem tpsSubsystem, String op, TokenRecord tokenRecord, String ip, String msg, String result, String uid) {
+            String op, TokenRecord tokenRecord, String ip, String msg, String result, String uid) {
         try {
-            tpsSubsystem.activityDatabase.log(
+            tps.activityDatabase.log(
                     ip,
                     (tokenRecord != null)? tokenRecord.getId():null,
                     op,
@@ -123,10 +134,10 @@ public class TPSTokendb {
         }
     }
 
-    public boolean isTokenPresent(TPSSubsystem tpsSubsystem, String cuid) {
+    public boolean isTokenPresent(String cuid) {
         boolean present = false;
         try {
-            tpsSubsystem.tokenDatabase.getRecord(cuid);
+            tps.tokenDatabase.getRecord(cuid);
             present = true;
         } catch (Exception e) {
             CMS.debug("TPSTokendb.isTokenPresent: token entry not found");
@@ -135,44 +146,130 @@ public class TPSTokendb {
         return present;
     }
 
-    public TokenRecord tdbGetTokenEntry(TPSSubsystem tpsSubsystem, String cuid)
+    public TokenRecord tdbGetTokenEntry(String cuid)
             throws Exception {
-        return tpsSubsystem.tokenDatabase.getRecord(cuid);
+        return tps.tokenDatabase.getRecord(cuid);
     }
 
-    public void tdbAddTokenEntry(TPSSubsystem tpsSubsystem, TokenRecord tokenRecord, String status)
+    /*
+     * tdbFindTokenRecordsByUID finds and returns token records belong to one user
+     * @param uid the uid of the owner to the token
+     * @return Hashtable of the token records with <cuid, TokenRecord>
+     */
+    public Hashtable<String, TokenRecord> tdbFindTokenRecordsByUID(String uid)
+            throws Exception {
+        Hashtable<String, TokenRecord> tokenRecords = new Hashtable<String, TokenRecord>();
+        String filter = uid;
+        Iterator<TokenRecord> records = null;
+        records = tps.tokenDatabase.findRecords(filter).iterator();
+
+       while (records.hasNext()) {
+           TokenRecord tokenRecord = records.next();
+           tokenRecords.put(tokenRecord.getId(), tokenRecord);
+       }
+
+       return tokenRecords;
+    }
+
+    public void tdbHasActiveToken(String userid)
+           throws Exception {
+        if (userid == null)
+            throw new Exception("TPSTokendb.tdbhasActiveToken: uerid null");
+
+        Hashtable<String, TokenRecord> tokens =
+                tdbFindTokenRecordsByUID(userid);
+        Set<String> keys = tokens.keySet();
+        boolean foundActive = false;
+        for (String key: keys) {
+            TokenRecord tokenRecord = tokens.get(key);
+            if (tokenRecord.getStatus().equals("active")) {
+                foundActive = true;
+            }
+        }
+        if (!foundActive) {
+            throw new Exception("TPSTokendb.tdbhasActiveToken: active token not found");
+        }
+    }
+
+    public void tdbAddTokenEntry(TokenRecord tokenRecord, String status)
             throws Exception {
         tokenRecord.setStatus(status);
 
-        tpsSubsystem.tokenDatabase.addRecord(tokenRecord.getId(), tokenRecord);
+        tps.tokenDatabase.addRecord(tokenRecord.getId(), tokenRecord);
     }
 
-    public void tdbUpdateTokenEntry(TPSSubsystem tpsSubsystem, TokenRecord tokenRecord)
+    public void tdbUpdateTokenEntry(TokenRecord tokenRecord)
             throws Exception {
         String id = tokenRecord.getId();
         TokenRecord existingTokenRecord;
         try {
-            existingTokenRecord = tpsSubsystem.tokenDatabase.getRecord(id);
+            existingTokenRecord = tps.tokenDatabase.getRecord(id);
         } catch (Exception e) {
             CMS.debug("TPSTokendb.tdbUpdateTokenEntry: token entry not found; Adding");
             // add and exit
-            tdbAddTokenEntry(tpsSubsystem, tokenRecord, tokenRecord.getStatus());
+            tdbAddTokenEntry(tokenRecord, "uninitialized");
             return;
         }
         // token found; modify
         CMS.debug("TPSTokendb.tdbUpdateTokenEntry: token entry found; Modifying with status: "+ tokenRecord.getStatus());
         // don't change the create time of an existing token record; put it back
         tokenRecord.setCreateTimestamp(existingTokenRecord.getCreateTimestamp());
-        tpsSubsystem.tokenDatabase.updateRecord(id, tokenRecord);
+        tps.tokenDatabase.updateRecord(id, tokenRecord);
     }
 
-    public void tdbUpdateCertificates(TPSSubsystem tpsSubsystem, String cuid, EnrolledCertsInfo certs)
-            throws Exception {
-        boolean tokenExist = isTokenPresent(tpsSubsystem, cuid);
+    /*
+     * tdbAddCertificatesForCUID adds certificates issued for the token CUID
+     * @param cuid the cuid of the token
+     * @return Hashtable of the cert records with <id, TPSCertRecord>
+     */
+    public void tdbAddCertificatesForCUID(String cuid, Hashtable<String, TPSCertRecord> certs)
+            throws TPSException {
+        boolean tokenExist = isTokenPresent(cuid);
         if (!tokenExist){
-            throw new Exception("TPSTokendb:tdbUpdateCertificates: token "+ cuid + " does not exist");
+            CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: token not found: "+ cuid);
+            throw new TPSException("TPSTokendb:tdbUpdateCertificates: token "+ cuid + " does not exist");
         }
 
+        CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: found token "+ cuid);
+        CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: number of certs to update:"+ certs.size());
+        Set<String> keys = certs.keySet();
+        try {
+            for (String key: keys) {
+                certs.get(key).setOrigin(cuid);
+                tps.certDatabase.addRecord(certs.get(key).getId(), certs.get(key));
+            }
+        } catch (Exception e) {
+            CMS.debug("TPSTokendb.tdbAddCertificatesForCUID: "+ e);
+            // TODO: what if it throws in the middle of the cert list -- some cert records already updated?
+            throw new TPSException(e.getMessage());
+        }
+    }
+
+    /*
+     * tdbGetCertificatesByCUID finds and returns certificate records belong to a token cuid
+     * @param cuid the cuid of the token
+     * @return Hashtable of the cert records with <id, TPSCertRecord>
+     */
+    public Hashtable<String, TPSCertRecord> tdbGetCertificatesByCUID(String cuid)
+            throws TPSException {
+        if (cuid == null)
+            throw new TPSException("TPSTokendb.tdbGetCertificatesByCUID: cuid null");
+
+        Hashtable<String, TPSCertRecord> certRecords = new Hashtable<String, TPSCertRecord>();
+        String filter = cuid;
+        Iterator<TPSCertRecord> records;
+        try {
+             records = tps.certDatabase.findRecords(filter).iterator();
+        } catch (Exception e) {
+            CMS.debug("TPSTokendb.tdbGetCertificatesByCUID:" + e);
+            throw new TPSException(e.getMessage());
+        }
+
+        while (records.hasNext()) {
+            TPSCertRecord certRecord = records.next();
+            certRecords.put(certRecord.getId(), certRecord);
+        }
 
+        return certRecords;
     }
 }
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 aa5cb542b6205194a3114dcd0023467682ec5761..bc92363b3b3127d484bacb665c16e741bcfb40a2 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
@@ -24,11 +24,7 @@ import java.security.cert.CertificateException;
 import java.util.Hashtable;
 import java.util.List;
 
-import netscape.security.x509.AuthorityKeyIdentifierExtension;
-import netscape.security.x509.KeyIdentifier;
-import netscape.security.x509.PKIXExtensions;
 import netscape.security.x509.RevocationReason;
-import netscape.security.x509.SubjectKeyIdentifierExtension;
 import netscape.security.x509.X509CertImpl;
 
 import org.dogtagpki.server.connector.IRemoteRequest;
@@ -316,11 +312,11 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
      * @returns CARevokeCertResponse
      */
     private CARevokeCertResponse revokeCertificate(
-            BigInteger serialno,
+            String serialno,
             RevocationReason reason)
             throws EBaseException {
 
-        CMS.debug("CARemoteRequestHandler: revokeCertificate(): begins.");
+        CMS.debug("CARemoteRequestHandler: revokeCertificate(): begins on serial#:"+ serialno);
         if (serialno == null || reason == null) {
             throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter null.");
         }
@@ -337,7 +333,7 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
                         IRemoteRequest.CA_OP + "=" + IRemoteRequest.CA_REVOKE +
                                 "&" + IRemoteRequest.CA_REVOCATION_REASON + "=" + reason.getCode() +
                                 "&" + IRemoteRequest.CA_REVOKE_ALL + "=(" +
-                                IRemoteRequest.CA_REVOKE_SERIAL + "=" + serialno.toString() + ")&" +
+                                IRemoteRequest.CA_REVOKE_SERIAL + "=" + serialno + ")&" +
                                 IRemoteRequest.CA_REVOKE_COUNT + "=1");
         String content = resp.getContent();
 
@@ -386,10 +382,10 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
      * @returns CARevokeCertResponse
      */
     private CARevokeCertResponse unrevokeCertificate(
-            BigInteger serialno)
+            String serialno)
             throws EBaseException {
 
-        CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): begins.");
+        CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): begins on serial#:"+ serialno);
         if (serialno == null) {
             throw new EBaseException("CARemoteRequestHandler: unrevokeCertificate(): input parameter null.");
         }
@@ -401,7 +397,7 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
         CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): sending request to CA");
         HttpResponse resp =
                 conn.send("unrevoke",
-                        IRemoteRequest.CA_UNREVOKE_SERIAL + "=" + serialno.toString());
+                        IRemoteRequest.CA_UNREVOKE_SERIAL + "=" + serialno);
         String content = resp.getContent();
 
         CMS.debug("CARemoteRequestHandler: unrevokeCertificate(): got content = " + content);
@@ -450,17 +446,36 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
      * @param cert cert to (un)revoke
      * @param serialno parameter for the (Un)RevokeCertificate() functions
      * @param reason RevocationReason for the base revokeCertificate() function
+     * @throws IOException
      */
+    @SuppressWarnings("unused")
     private CARevokeCertResponse revokeFromOtherCA(
             boolean revoke, // true==revoke; false==unrevoke
             X509CertImpl cert,
             RevocationReason reason)
-            throws EBaseException {
-
-        CMS.debug("CARemoteRequestHandler: revokeFromOtherCA: begins");
+            throws EBaseException, IOException {
         if (cert == null) {
             throw new EBaseException("CARemoteRequestHandler: revokeFromOtherCA(): input parameter cert null.");
         }
+        String certAkiString = null;
+        try {
+            certAkiString = Util.getCertAkiString(cert);
+        } catch (Exception e) {
+            throw new EBaseException("CARemoteRequestHandler: revokeFromOtherCA(): getCertAkiString failed:" + e);
+        }
+        return revokeFromOtherCA(revoke, cert.getSerialNumber().toString(), certAkiString, reason);
+    }
+
+
+    private CARevokeCertResponse revokeFromOtherCA(
+            boolean revoke, // true==revoke; false==unrevoke
+            String serialno,
+            String certAkiString,
+            RevocationReason reason)
+            throws EBaseException {
+
+
+        CMS.debug("CARemoteRequestHandler: revokeFromOtherCA: begins");
 
         TPSSubsystem subsystem =
                 (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
@@ -468,12 +483,6 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
                 subsystem.getConnectionManager().getCAList();
 
         Exception exception = null;
-        String certAkiString = null;
-        try {
-            certAkiString = getCertAkiString(cert);
-        } catch (Exception e) {
-            exception = e;
-        }
 
         for (String ca : caList) {
             try {
@@ -481,9 +490,9 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
                 if (certAkiString.equals(caSkiString)) {
                     CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() cert AKI and caCert SKI matched");
                     if (revoke) {
-                        return revokeCertificate(cert.getSerialNumber(), reason);
+                        return revokeCertificate(serialno, reason);
                     } else {
-                        return unrevokeCertificate(cert.getSerialNumber());
+                        return unrevokeCertificate(serialno);
                     }
                 } else { // not a match then iterate to next ca in list
                     CMS.debug("CARemoteRequestHandler: revokeFromOtherCA() cert AKI and caCert SKI not matched");
@@ -526,7 +535,6 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
          * config store. If not, put it in, so we don't have to
          * calculate that every time.
          */
-        String caSKI = null;
         try {
             String configName = "tps.connector." + conn + ".caSKI";
             CMS.debug("CARemoteRequestHandler: getCaSki() retriving configName=" + configName);
@@ -548,7 +556,7 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
                 X509Certificate c = cm.findCertByNickname(caNickname);
                 X509CertImpl caCert = new X509CertImpl(c.getEncoded());
                 // now retrieve caSKI and store in config
-                caSkiString = getCertSkiString(caCert);
+                caSkiString = Util.getCertSkiString(caCert);
                 CMS.debug("CARemoteRequestHandler: getCaSki() caSKI calculated. Saving it.");
                 conf.putString("tps.connector." + conn + ".caSKI", caSkiString);
                 conf.commit(false);
@@ -577,31 +585,7 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
         return caSkiString;
     }
 
-    private String getCertAkiString(X509CertImpl cert)
-            throws EBaseException, IOException {
-        if (cert == null) {
-            throw new EBaseException("CARemoteRequestHandler: getCertAkiString(): input parameter cert null.");
-        }
-        AuthorityKeyIdentifierExtension certAKI =
-                (AuthorityKeyIdentifierExtension)
-                cert.getExtension(PKIXExtensions.AuthorityKey_Id.toString());
-        KeyIdentifier kid =
-                (KeyIdentifier) certAKI.get(AuthorityKeyIdentifierExtension.KEY_ID);
-        return (CMS.BtoA(kid.getIdentifier()).trim());
-    }
 
-    private String getCertSkiString(X509CertImpl cert)
-            throws EBaseException, IOException {
-        if (cert == null) {
-            throw new EBaseException("CARemoteRequestHandler: getCertSkiString(): input parameter cert null.");
-        }
-        SubjectKeyIdentifierExtension certSKI =
-                (SubjectKeyIdentifierExtension)
-                cert.getExtension(PKIXExtensions.SubjectKey_Id.toString());
-        KeyIdentifier kid =
-                (KeyIdentifier) certSKI.get(SubjectKeyIdentifierExtension.KEY_ID);
-        return (CMS.BtoA(kid.getIdentifier()).trim());
-    }
 
     /**
      * revokeCertificate() supports revocation routing by providing
@@ -624,11 +608,28 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
             X509CertImpl cert,
             RevocationReason reason)
             throws EBaseException {
-
-        CMS.debug("CARemoteRequestHandler: revokeCertificate() begins with CA discovery");
         if (cert == null) {
             throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter cert null.");
         }
+        String certAkiString = null;
+        try {
+            certAkiString = Util.getCertAkiString(cert);
+        } catch (IOException e) {
+            throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): getCertAkiString failed:" + e);
+        }
+
+        return revokeCertificate(revoke, cert.getSerialNumber().toString(), certAkiString, reason);
+    }
+
+    public CARevokeCertResponse revokeCertificate(
+            boolean revoke, // true==revoke; false==unrevoke
+            String serialno,
+            String certAkiString,
+            RevocationReason reason)
+            throws EBaseException {
+
+        CMS.debug("CARemoteRequestHandler: revokeCertificate() begins with CA discovery");
+
         if (revoke == true && reason == null) {
             throw new EBaseException("CARemoteRequestHandler: revokeCertificate(): input parameter reason null.");
         }
@@ -636,11 +637,9 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
         boolean skipMatch = false;
 
         String caSkiString = null;
-        String certAkiString = null;
 
         try {
             caSkiString = getCaSki(connid);
-            certAkiString = getCertAkiString(cert);
         } catch (Exception e) {
             CMS.debug("CARemoteRequestHandler: revokeCertificate() exception:" + e);
             skipMatch = true;
@@ -654,19 +653,19 @@ public class CARemoteRequestHandler extends RemoteRequestHandler
             if (certAkiString.equals(caSkiString)) {
                 CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI matched");
                 if (revoke) {
-                    return revokeCertificate(cert.getSerialNumber(), reason);
+                    return revokeCertificate(serialno, reason);
                 } else {
-                    return unrevokeCertificate(cert.getSerialNumber());
+                    return unrevokeCertificate(serialno);
                 }
             } else {
                 CMS.debug("CARemoteRequestHandler: revokeCertificate() cert AKI and caCert SKI of the designated issuing ca do not match...calling revokeFromOtherCA to search for another ca");
-                return revokeFromOtherCA(revoke, cert, reason);
+                return revokeFromOtherCA(revoke, serialno, certAkiString, reason);
             }
         } else {
             if (revoke) {
-                return revokeCertificate(cert.getSerialNumber(), reason);
+                return revokeCertificate(serialno, reason);
             } else {
-                return unrevokeCertificate(cert.getSerialNumber());
+                return unrevokeCertificate(serialno);
             }
         }
     }
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java
index 9589139539d48a90ee2bfafa6c8814e3560f6870..288f25f53518d713500fdb01ca802d7d6d74a8b0 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/dbs/TPSCertRecord.java
@@ -58,6 +58,7 @@ public class TPSCertRecord extends DBRecord {
         this.id = id;
     }
 
+    // the serial number is in HEX
     @DBAttribute("tokenSerial")
     public String getSerialNumber() {
         return serialNumber;
@@ -113,10 +114,12 @@ public class TPSCertRecord extends DBRecord {
     }
 
     @DBAttribute("userCertificate")
+    // Alternative to the actual certificate -- certificate AKI
     public String getCertificate() {
         return certificate;
     }
 
+    // Alternative to the actual certificate -- certificate AKI
     public void setCertificate(String certificate) {
         this.certificate = certificate;
     }
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java
index 427e04e731f1c5933f29fe70ffe01e67f17a8d1e..4e8362e3a8824d9d5bf1182b0013622a3d9e3843 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/processor/EnrolledCertsInfo.java
@@ -17,12 +17,20 @@
 // --- END COPYRIGHT BLOCK ---
 package org.dogtagpki.server.tps.processor;
 
+import java.io.IOException;
+import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Hashtable;
 
 import netscape.security.x509.X509CertImpl;
 
+import org.dogtagpki.server.tps.dbs.TPSCertRecord;
 import org.dogtagpki.server.tps.main.PKCS11Obj;
 import org.dogtagpki.tps.main.TPSBuffer;
+import org.dogtagpki.tps.main.Util;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.EBaseException;
 
 public class EnrolledCertsInfo {
 
@@ -146,4 +154,108 @@ public class EnrolledCertsInfo {
 
     }
 
+    public Hashtable<String, TPSCertRecord> toTPSCertRecords(String cuid, String uid) {
+        Hashtable<String, TPSCertRecord> certs = new Hashtable<String, TPSCertRecord>();
+        CMS.debug("EnrolledCertsInfo.toTPSCertRecords: starts");
+        int index = 0;
+        for (X509CertImpl cert: certificates) {
+            TPSCertRecord certRecord = new TPSCertRecord();
+
+            //serial number
+            BigInteger serial_BigInt = cert.getSerialNumber();
+
+            String hexSerial = serial_BigInt.toString(16);
+            String serialNumber = "0x" + hexSerial;
+            certRecord.setSerialNumber(serialNumber);
+
+            //id - TODO - calculate the real id later
+            String id = serialNumber+":"+uid;
+
+            certRecord.setId(id);
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: converting cert:"+ certRecord.getId());
+
+            //token id
+            certRecord.setTokenID(cuid);
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: cuid =" + cuid);
+
+            //origin
+            if ((!origins.isEmpty()) && index <origins.size() && origins.get(index)!= null) {
+                certRecord.setOrigin(origins.get(index));
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: origin =" + origins.get(index));
+            } else {
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: origin not found for index:"+ index);
+            }
+
+            //user id
+            certRecord.setUserID(uid);
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: uid =" + uid);
+
+            //KeyType
+            if ((!ktypes.isEmpty()) && index <ktypes.size() && ktypes.get(index)!= null) {
+                certRecord.setKeyType(ktypes.get(index));
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: keyType =" + ktypes.get(index));
+            } else {
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: keyType not found for index:"+ index);
+            }
+
+            //token type
+            if ((!tokenTypes.isEmpty()) && index <tokenTypes.size() && tokenTypes.get(index)!= null) {
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: tokenType=" + tokenTypes.get(index));
+                certRecord.setType(tokenTypes.get(index));
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: tokenType set");
+            } else {
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: tokenType not found for index:"+ index);
+                //certRecord.setType("");
+            }
+
+            //Issuer
+            String issuedBy = cert.getIssuerDN().toString();
+            certRecord.setIssuedBy(issuedBy);
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: issuer ="+ issuedBy);
+
+            //Subject
+            String subject = cert.getSubjectDN().toString();
+            certRecord.setSubject(subject);
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: subject ="+ subject);
+
+            //NotBefore
+            certRecord.setValidNotBefore(cert.getNotBefore());
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: notBefore ="+ cert.getNotBefore().toString());
+
+            //NotAfter
+            certRecord.setValidNotAfter(cert.getNotAfter());
+            CMS.debug("EnrolledCertsInfo.toTPSCertRecords: notAfter ="+ cert.getNotAfter().toString());
+
+            //status
+            certRecord.setStatus("active");
+
+            /* certificate
+            byte[] certBytes = null;
+            try {
+                certBytes = cert.getEncoded();
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecords: certBytes ="+ CMS.BtoA(certBytes));
+            } catch (CertificateEncodingException e) {
+                CMS.debug("EnrolledCertsInfo.toTPSCertRecord: "+ e);
+                //TODO: throw
+
+            }
+            certRecord.setCertificate(CMS.BtoA(certBytes));
+            */
+            // Alternative to the actual certificate -- certificate AKI
+            try {
+                String aki = Util.getCertAkiString(cert);
+                certRecord.setCertificate(aki);
+            } catch (EBaseException | IOException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+
+            certs.put(id, certRecord);
+
+            index++;
+        }
+        CMS.debug("EnrolledCertsInfo.toTPSCertRecords: ends");
+        return certs;
+    }
+
 }
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 9bc831b574b9e0ea096e82c7cb6438a4466446df..b9a5df397e58c189c824793f818738f3f3cb5d7a 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
@@ -3,9 +3,11 @@ package org.dogtagpki.server.tps.processor;
 import java.io.IOException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
+import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Random;
+import java.util.Set;
 import java.util.zip.DataFormatException;
 
 import netscape.security.provider.RSAPublicKey;
@@ -15,6 +17,7 @@ import netscape.security.x509.X509CertImpl;
 
 import org.dogtagpki.server.tps.TPSSession;
 import org.dogtagpki.server.tps.TPSSubsystem;
+import org.dogtagpki.server.tps.TPSTokenPolicy;
 import org.dogtagpki.server.tps.authentication.TPSAuthenticator;
 import org.dogtagpki.server.tps.channel.SecureChannel;
 import org.dogtagpki.server.tps.channel.SecureChannel.TokenKeyType;
@@ -22,6 +25,7 @@ import org.dogtagpki.server.tps.cms.CAEnrollCertResponse;
 import org.dogtagpki.server.tps.cms.CARemoteRequestHandler;
 import org.dogtagpki.server.tps.cms.KRAServerSideKeyGenResponse;
 import org.dogtagpki.server.tps.dbs.ActivityDatabase;
+import org.dogtagpki.server.tps.dbs.TPSCertRecord;
 import org.dogtagpki.server.tps.dbs.TokenRecord;
 import org.dogtagpki.server.tps.engine.TPSEngine;
 import org.dogtagpki.server.tps.main.ObjectSpec;
@@ -31,6 +35,7 @@ import org.dogtagpki.tps.main.TPSBuffer;
 import org.dogtagpki.tps.main.TPSException;
 import org.dogtagpki.tps.main.Util;
 import org.dogtagpki.tps.msg.BeginOpMsg;
+import org.dogtagpki.tps.msg.EndOpMsg;
 import org.dogtagpki.tps.msg.EndOpMsg.TPSStatus;
 import org.mozilla.jss.asn1.InvalidBERException;
 import org.mozilla.jss.crypto.InvalidKeyFormatException;
@@ -42,6 +47,7 @@ import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.authentication.IAuthCredentials;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.EPropertyNotFound;
 import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.tps.token.TokenStatus;
 import com.netscape.cmsutil.util.Utils;
@@ -69,8 +75,8 @@ public class TPSEnrollProcessor extends TPSProcessor {
     private void enroll() throws TPSException, IOException {
         CMS.debug("TPSEnrollProcessor enroll: entering...");
         String auditMsg = null;
-        TPSEngine engine = getTPSEngine();
         TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        TPSTokenPolicy tokenPolicy = new TPSTokenPolicy(null);
 
         AppletInfo appletInfo = null;
         TokenRecord tokenRecord = null;
@@ -78,14 +84,26 @@ public class TPSEnrollProcessor extends TPSProcessor {
             appletInfo = getAppletInfo();
         } catch (TPSException e) {
             auditMsg = e.toString();
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(),
-                    auditMsg, "failure");
+            tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg,
+                    "failure");
 
             throw e;
         }
         appletInfo.setAid(getCardManagerAID());
+
+        CMS.debug("TPSEnrollProcessor.enroll: token cuid: " + appletInfo.getCUIDhexStringPlain());
         boolean isTokenPresent = false;
-        tokenRecord = createTokenRecord(isTokenPresent, appletInfo);
+        try {
+            tokenRecord = tps.tdb.tdbGetTokenEntry(appletInfo.getCUIDhexStringPlain());
+            // now the in memory tokenRecord is replaced by the actual token data
+            CMS.debug("TPSEnrollProcessor.enroll: found token...");
+            isTokenPresent = true;
+        } catch (Exception e) {
+            CMS.debug("TPSEnrollProcessor.enroll: token does not exist in tokendb... create one in memory");
+            tokenRecord = new TokenRecord();
+            tokenRecord.setId(appletInfo.getCUIDhexStringPlain());
+        }
+        fillTokenRecord(tokenRecord, appletInfo);
         session.setTokenRecord(tokenRecord);
 
         String resolverInstName = getResolverInstanceName();
@@ -97,10 +115,11 @@ public class TPSEnrollProcessor extends TPSProcessor {
         CMS.debug("TPSEnrollProcessor.enroll: resolved tokenType: " + tokenType);
 
         checkProfileStateOK();
+        String cuid = appletInfo.getCUIDhexStringPlain();
 
         boolean do_force_format = false;
         if (isTokenPresent) {
-            CMS.debug("TPSEnrollProcessor.enroll: token exists");
+            CMS.debug("TPSEnrollProcessor.enroll: token exists in tokendb");
 
             TokenStatus newState = TokenStatus.ACTIVE;
             // Check for transition to ACTIVE status.
@@ -108,11 +127,11 @@ public class TPSEnrollProcessor extends TPSProcessor {
                 CMS.debug("TPSEnrollProcessor.enroll: token transition disallowed " +
                         tokenRecord.getTokenStatus() +
                         " to " + newState);
-                auditMsg = "Operation for CUID " + appletInfo.getCUIDhexStringPlain() +
+                auditMsg = "Operation for CUID "+ cuid +
                         " Disabled, illegal transition attempted " + tokenRecord.getTokenStatus() +
                         " to " + newState;
-                tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(),
-                        auditMsg, "failure");
+                tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg,
+                        "failure");
 
                 throw new TPSException(auditMsg,
                         TPSStatus.STATUS_ERROR_DISABLED_TOKEN);
@@ -121,7 +140,19 @@ public class TPSEnrollProcessor extends TPSProcessor {
                         tokenRecord.getTokenStatus() +
                         " to " + newState);
             }
-            do_force_format = engine.raForceTokenFormat(appletInfo.getCUIDhexString());
+            do_force_format = tokenPolicy.isForceTokenFormat(cuid);
+
+            if (!tokenPolicy.isAllowdTokenReenroll(cuid) &&
+                    !tokenPolicy.isAllowdTokenRenew(cuid)) {
+                CMS.debug("TPSEnrollProcessor.enroll: token renewal or reEnroll disallowed ");
+                auditMsg = "Operation renewal or reEnroll for CUID "+ cuid +
+                        " Disabled";
+                tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg,
+                            "failure");
+
+                throw new TPSException(auditMsg,
+                        TPSStatus.STATUS_ERROR_DISABLED_TOKEN);
+            }
         } else {
             CMS.debug("TPSEnrollProcessor.enroll: token does not exist");
             tokenRecord.setStatus("uninitialized");
@@ -162,27 +193,24 @@ public class TPSEnrollProcessor extends TPSProcessor {
             pkcs11objx = getCurrentObjectsOnToken(channel);
         } catch (DataFormatException e) {
             auditMsg = "TPSEnrollProcessor.enroll: Failed to parse original token data: " + e.toString();
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(),
-                    auditMsg, "failure");
+            tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg,
+                    "failure");
 
             throw new TPSException(auditMsg);
         }
 
         pkcs11objx.setCUID(appletInfo.getCUID());
 
-        try {
-            tokenRecord.setStatus("active");
-            String successMsg = "update token success";
-            tps.tdb.tdbUpdateTokenEntry(tps, tokenRecord);
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(),
-                    successMsg, "success");
-        } catch (Exception e) {
-            String failMsg = "update token failure";
-            auditMsg = failMsg + ":" + e.toString();
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(),
-                    failMsg, "failure");
-            throw new TPSException(auditMsg);
+        if (!isTokenPresent) {
+            try {
+                tps.tdb.tdbAddTokenEntry(tokenRecord, "uninitialized");
+            } catch (Exception e) {
+                String failMsg = "add token failure";
+                auditMsg = failMsg + ":" + e.toString();
+                throw new TPSException(auditMsg);
+            }
         }
+
         statusUpdate(10, "PROGRESS_PROCESS_PROFILE");
 
         EnrolledCertsInfo certsInfo = new EnrolledCertsInfo();
@@ -192,8 +220,41 @@ public class TPSEnrollProcessor extends TPSProcessor {
         certsInfo.setStartProgress(15);
         certsInfo.setEndProgress(90);
 
-        generateCertificates(certsInfo, channel, appletInfo);
-
+        boolean renewed = false;
+        TPSStatus status = generateCertsAfterRenewalRecoveryPolicy(certsInfo, channel, appletInfo);
+        //anything failed would have thrown an exception
+        String statusString = "Unknown"; // gives some meaningful debug message
+        if (status == TPSStatus.STATUS_NO_ERROR)
+            statusString = "Enrollment to follow";
+        else if (status == TPSStatus.STATUS_ERROR_RECOVERY_IS_PROCESSED)
+            statusString = "Recovery processed";
+        else if (status == TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED)
+            statusString = "Renewal processed";
+        auditMsg = "generateCertsAfterRenewalRecoveryPolicy returns status:"
+                + EndOpMsg.statusToInt(status) + " : " + statusString;
+        CMS.debug("TPSEnrollProcessor.enroll: " + auditMsg);
+        if (status == TPSStatus.STATUS_NO_ERROR) {
+            if (!generateCertificates(certsInfo, channel, appletInfo)) {
+                CMS.debug("TPSEnrollProcessor.enroll:generateCertificates returned false means some certs failed enrollment;  clean up (format) the token");
+                format(true /*skipAuth*/);
+                throw new TPSException("generateCertificates failed");
+            } else {
+                CMS.debug("TPSEnrollProcessor.enroll:generateCertificates returned true means cert enrollment successful");
+            }
+        }
+        // at this point, enrollment, renewal, or recovery have been processed accordingly;
+        if (status == TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED &&
+                tokenPolicy.isAllowdTokenRenew(cuid)) {
+            renewed = true;
+            CMS.debug("TPSEnrollProcessor.enroll: renewal happened.. ");
+        }
+/*
+ * TODO:
+ * find the point to do the following...
+ * when total available memory is exceeded on the token ...
+ *     if(!renewed) //Renewal should leave what they have on the token.
+ *         format(true);
+ */
         String tokenLabel = buildTokenLabel(certsInfo, appletInfo);
 
         pkcs11objx.setTokenName(new TPSBuffer(tokenLabel.getBytes()));
@@ -228,11 +289,27 @@ public class TPSEnrollProcessor extends TPSProcessor {
         statusUpdate(99, "PROGRESS_SET_LIFECYCLE");
         channel.setLifeycleState((byte) 0x0f);
 
-        auditMsg = "enroll operation succeeded";
-        tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg,
-                "success");
-        //nothing to do here; log it and continue
-        CMS.debug(auditMsg);
+        try {
+            tokenRecord.setStatus("active");
+            tps.tdb.tdbUpdateTokenEntry(tokenRecord);
+        } catch (Exception e){
+            String failMsg = "update token failure";
+            auditMsg =  failMsg + ":" + e.toString();
+            tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), failMsg,
+                    "failure");
+            throw new TPSException(auditMsg);
+        }
+        //update the tokendb with new certs
+        CMS.debug("TPSEnrollProcessor.enroll: updating tokendb with certs.");
+        Hashtable<String, TPSCertRecord> certRecords = certsInfo.toTPSCertRecords(tokenRecord.getId(), tokenRecord.getUserID());
+        tps.tdb.tdbAddCertificatesForCUID(tokenRecord.getId(), certRecords);
+
+        auditMsg = "appletVersion=" + lastObjVer + "; tokenType =" + selectedTokenType + "; userid =" + userid;
+        if (renewed) {
+            tps.tdb.tdbActivity(ActivityDatabase.OP_RENEWAL, tokenRecord, session.getIpAddress(), auditMsg, "success");
+        } else {
+            tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), auditMsg, "success");
+        }
 
         CMS.debug("TPSEnrollProcessor.enroll: leaving ...");
 
@@ -337,8 +414,8 @@ public class TPSEnrollProcessor extends TPSProcessor {
                     // all exceptions are considered login failure
                     CMS.debug("TPSEnrollProcessor.checkAndAuthenticateUser:: authentication exception thrown: " + e);
                     String msg = "TPS error user authentication failed:" + e;
-                    tps.tdb.tdbActivity(tps, ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(),
-                            msg, "failure");
+                    tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), msg,
+                            "failure");
 
                     throw new TPSException(msg,
                             TPSStatus.STATUS_ERROR_LOGIN);
@@ -463,14 +540,14 @@ public class TPSEnrollProcessor extends TPSProcessor {
 
     protected boolean checkUpdateAppletEncryption() throws TPSException {
 
-        CMS.debug("TPSProcessor.checkUpdateAppletEncryption entering...");
+        CMS.debug("TPSEnrollProcessor.checkUpdateAppletEncryption entering...");
 
         IConfigStore configStore = CMS.getConfigStore();
 
         String appletEncryptionConfig = "op." + currentTokenOperation + "." + selectedTokenType + "."
                 + TPSEngine.CFG_UPDATE_APPLET_ENCRYPTION;
 
-        CMS.debug("TPSProcessor.checkUpdateAppletEncryption config to check: " + appletEncryptionConfig);
+        CMS.debug("TPSEnrollProcessor.checkUpdateAppletEncryption config to check: " + appletEncryptionConfig);
 
         boolean appletEncryption = false;
 
@@ -479,10 +556,10 @@ public class TPSEnrollProcessor extends TPSProcessor {
         } catch (EBaseException e) {
             //Default TPSException will return a "contact admin" error code.
             throw new TPSException(
-                    "TPSProcessor.checkUpdateAppletEncryption: internal error in getting value from config.");
+                    "TPSEnrollProcessor.checkUpdateAppletEncryption: internal error in getting value from config.");
         }
 
-        CMS.debug("TPSProcessor.checkUpdateAppletEncryption returning: " + appletEncryption);
+        CMS.debug("TPSEnrollProcessor.checkUpdateAppletEncryption returning: " + appletEncryption);
         return appletEncryption;
 
     }
@@ -561,11 +638,188 @@ public class TPSEnrollProcessor extends TPSProcessor {
         return pkcs11objx;
     }
 
+    /*
+     * generateCertsAfterRenewalRecoveryPolicy determines whether a renewal or recovery is needed;
+     * if recovery is needed, it determines which certificates (from which old token)
+     *  to recover onto the new token.
+     *
+     * Note: renewal and recovery are invoked in this method;  However, if a new enrollment is determined
+     * to be the proper course of action, it is done after this method.
+     */
+    private TPSStatus generateCertsAfterRenewalRecoveryPolicy(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo)
+            throws TPSException {
+        TPSStatus status = TPSStatus.STATUS_NO_ERROR;
+        String auditMsg;
+        final String method = "TPSEnrollProcessor.generateCertsAfterRenewalRecoveryPolicy";
+        CMS.debug(method + ": begins");
+        TPSSubsystem tps =
+                (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        TPSTokenPolicy tokenPolicy = new TPSTokenPolicy(tps);
+
+        Hashtable<String, TokenRecord> tokenRecords = null;
+        try {
+            tokenRecords = tps.tdb.tdbFindTokenRecordsByUID(userid);
+        } catch (Exception e) {
+            // no existing record, means no "renewal" or "recovery" actions needed
+            auditMsg = "no token associated with user: " + userid;
+            CMS.debug(method + auditMsg);
+            throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_INACTIVE_TOKEN_NOT_FOUND);
+        }
+        Set<String> keys = tokenRecords.keySet();
+        CMS.debug(method + " found " + keys.size() + " tokens for user:" + userid);
+        boolean isRecover = false;
+
+        for (String key: keys) {
+            TokenRecord tokenRecord = tokenRecords.get(key);
+            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?
+            if (tokenRecord.getId().equals(aInfo.getCUIDhexStringPlain())) {
+                //same token
+                if (tokenRecord.getStatus().equals("uninitialized")) {
+                    if (keys.size() == 1) {
+                        CMS.debug(method +  ": need to do enrollment");
+                        // need to do enrollment outside
+                        break;
+                    } else {
+                        CMS.debug(method +  ": There are multiple token entries for user "
+                                + userid);
+                        try {
+                            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
+                                /*
+                                 * current token is in active state
+                                 * 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;
+                        }
+                    }
+                } else if (tokenRecord.getStatus().equals("active")) {
+                    if (tokenPolicy.isAllowdTokenRenew(aInfo.getCUIDhexStringPlain())) {
+                        return processRenewal();                    }
+                    break;
+                } else if (tokenRecord.getStatus().equals("terminated")) {
+                    auditMsg = method + ": terminated token cuid="
+                            + aInfo.getCUIDhexStringPlain();
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_CONTACT_ADMIN);
+                } else if (tokenRecord.getStatus().equals("lost")) {
+                    String reasonStr = tokenRecord.getReason();
+                    if (reasonStr.equals("keyCompromise")) {
+                        auditMsg = "This token cannot be reused because it has been reported lost";
+                        CMS.debug(method + ": "
+                                + auditMsg);
+                        throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_UNUSABLE_TOKEN_KEYCOMPROMISE);
+                    } else if (reasonStr.equals("onHold")) {
+                        try {
+                            tps.tdb.tdbHasActiveToken(userid);
+                            auditMsg = "user already has an active token";
+                            CMS.debug(method + ": "
+                                    + auditMsg);
+                            throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_HAS_AT_LEAST_ONE_ACTIVE_TOKEN);
+                        } catch (Exception e2) {
+                            auditMsg = "User needs to contact administrator to report lost token (it should be put on Hold).";
+                            CMS.debug(method + ": "
+                                    + auditMsg);
+                            break;
+                        }
+                    } else if (reasonStr.equals("destroyed")) {
+                        auditMsg = "This destroyed lost case should not be executed because the token is so damaged. It should not get here";
+                        CMS.debug(method + ": "
+                                + auditMsg);
+                        throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_TOKEN_DISABLED);
+                    } 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);
+                    }
+
+                } else {
+                    auditMsg = "No such token status for this cuid=" + aInfo.getCUIDhexStringPlain();
+                    CMS.debug(method + ":" + auditMsg);
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_NO_SUCH_TOKEN_STATE);
+                }
+            } else { //cuid != current token
+                continue;
+            }
+        }
+
+        CMS.debug(method + ": ends");
+        return status;
+    }
+
+    private TPSStatus processRenewal() {
+        TPSStatus status = TPSStatus.STATUS_ERROR_RENEWAL_IS_PROCESSED;
+
+        // TODO Auto-generated method stub
+        CMS.debug("TPSEnrollProcess.processRenewal: reached");
+
+        return status;
+    }
+
+    private TPSStatus processRecovery() {
+        TPSStatus status = TPSStatus.STATUS_ERROR_RECOVERY_IS_PROCESSED;
+
+        // TODO Auto-generated method stub
+        CMS.debug("TPSEnrollProcess.processRecovery: reached");
+
+        return status;
+    }
+
     //Stub to generate a certificate, more to come
-    private void generateCertificates(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo)
+    private boolean generateCertificates(EnrolledCertsInfo certsInfo, SecureChannel channel, AppletInfo aInfo)
             throws TPSException, IOException {
 
-        CMS.debug("TPSProcess.generateCertificates: begins ");
+        CMS.debug("TPSEnrollProcess.generateCertificates: begins ");
+        boolean noFailedCerts = true;
+
         if (certsInfo == null || aInfo == null || channel == null) {
             throw new TPSException("TPSEnrollProcessor.generateCertificates: Bad Input data!",
                     TPSStatus.STATUS_ERROR_MAC_ENROLL_PDU);
@@ -580,10 +834,27 @@ public class TPSEnrollProcessor extends TPSProcessor {
         for (int i = 0; i < keyTypeNum; i++) {
             String keyType = getConfiguredKeyType(i);
             certsInfo.setCurrentCertIndex(i);
-            generateCertificate(certsInfo, channel, aInfo, keyType);
+            try {
+                generateCertificate(certsInfo, channel, aInfo, keyType);
+            } catch (TPSException e) {
+                CMS.debug("TPSEnrollProcessor.generateCertificate: exception:" + e);
+                noFailedCerts = false;
+                break; //need to clean up half-done token later
+            }
         }
 
-        CMS.debug("TPSProcess.generateCertificates: ends ");
+        /*
+         * In this special case of RE_ENROLL, Revoke current certs for this token
+         * if so configured
+         */
+        /*TODO: format that follows should do this already based on the returned noFailedCerts value
+        if (noFailedCerts == true) {
+            revokeCertificates(aInfo.getCUIDhexStringPlain());
+        }
+        */
+
+        CMS.debug("TPSEnrollProcessor.generateCertificates: ends ");
+        return noFailedCerts;
     }
 
     private String buildTokenLabel(EnrolledCertsInfo certsInfo, AppletInfo ainfo) throws TPSException {
@@ -1498,11 +1769,11 @@ public class TPSEnrollProcessor extends TPSProcessor {
         try {
             id = configStore.getString(config, "kra1");
         } catch (EBaseException e) {
-            throw new TPSException("TPSProcessor.getDRMConnectorID: Internal error finding config value.");
+            throw new TPSException("TPSEnrollProcessor.getDRMConnectorID: Internal error finding config value.");
 
         }
 
-        CMS.debug("TPSProcessor.getDRMConectorID: returning: " + id);
+        CMS.debug("TPSEnrollProcessor.getDRMConectorID: returning: " + id);
 
         return id;
     }
@@ -1533,24 +1804,6 @@ public class TPSEnrollProcessor extends TPSProcessor {
         return keyTypeNum;
     }
 
-    protected String getCAConnectorID() throws TPSException {
-        IConfigStore configStore = CMS.getConfigStore();
-        String id = null;
-
-        String config = "op." + currentTokenOperation + "." + selectedTokenType + ".ca.conn";
-
-        try {
-            id = configStore.getString(config, "ca1");
-        } catch (EBaseException e) {
-            throw new TPSException("TPSEnrollProcessor.getCAConnectorID: Internal error finding config value.");
-
-        }
-
-        CMS.debug("TPSEnrollProcessor.getCAConectorID: returning: " + id);
-
-        return id;
-    }
-
     private TPSBuffer makeKeyIDFromPublicKeyInfo(byte[] publicKeyInfo) throws TPSException {
 
         final String alg = "SHA1";
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 94cab4a293be399b7d88ed7639f646310594a408..ab61c19a1e83fab9d4d87c31a21530d3528d929e 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
@@ -19,25 +19,32 @@ package org.dogtagpki.server.tps.processor;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 
+import netscape.security.x509.RevocationReason;
+
 import org.dogtagpki.server.tps.TPSSession;
 import org.dogtagpki.server.tps.TPSSubsystem;
 import org.dogtagpki.server.tps.authentication.AuthUIParameter;
 import org.dogtagpki.server.tps.authentication.TPSAuthenticator;
 import org.dogtagpki.server.tps.channel.SecureChannel;
+import org.dogtagpki.server.tps.cms.CARemoteRequestHandler;
+import org.dogtagpki.server.tps.cms.CARevokeCertResponse;
 import org.dogtagpki.server.tps.cms.TKSComputeRandomDataResponse;
 import org.dogtagpki.server.tps.cms.TKSComputeSessionKeyResponse;
 import org.dogtagpki.server.tps.cms.TKSEncryptDataResponse;
 import org.dogtagpki.server.tps.cms.TKSRemoteRequestHandler;
 import org.dogtagpki.server.tps.dbs.ActivityDatabase;
+import org.dogtagpki.server.tps.dbs.TPSCertRecord;
 import org.dogtagpki.server.tps.dbs.TokenRecord;
 import org.dogtagpki.server.tps.engine.TPSEngine;
 import org.dogtagpki.server.tps.profile.BaseTokenProfileResolver;
@@ -904,33 +911,19 @@ public class TPSProcessor {
     }
 
     /*
-     * createTokenRecord -
+     * fillTokenRecord -
      *     - retrieves token record from tokendb if it exists, or
      *     - creates a new token record
-     *     - sets the isTokenPresent to true if token found in tokendb
      *     this in-memory copy of tokenRecord is to be set in the TPSSession
      */
-    protected TokenRecord createTokenRecord(boolean isTokenPresent, AppletInfo appletInfo)
+    protected void fillTokenRecord(TokenRecord tokenRecord, AppletInfo appletInfo)
             throws TPSException {
-        CMS.debug("TPSProcessor.createTokenRecord: begins");
-        if (appletInfo == null) {
-            CMS.debug("TPSProcessor.createTokenRecord: param appletInfo cannot be null");
+        String method = "TPSProcessor.fillTokenRecord";
+        CMS.debug(method + ": begins");
+        if (tokenRecord == null || appletInfo == null) {
+            CMS.debug(method + ": params tokenRecord and appletInfo cannot be null");
             throw new TPSException(
-                    "TPSProcessor.requestExtendedLogin: missing parameter(s): parameter appletInfo");
-        }
-        TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
-        TokenRecord tokenRecord = null;
-
-        try {
-            TokenRecord gotToken = tps.tdb.tdbGetTokenEntry(tps, appletInfo.getCUIDhexStringPlain());
-            // now the in memory tokenRecord is replaced by the actual token data
-            tokenRecord = gotToken;
-            CMS.debug("TPSProcessor.createTokenRecord: found token...");
-            isTokenPresent = true;
-        } catch (Exception e) {
-            CMS.debug("TPSProcessor.createTokenRecord: token does not exist...");
-            tokenRecord = new TokenRecord();
-            tokenRecord.setId(appletInfo.getCUIDhexStringPlain());
+                    method + ": missing parameter(s): parameter appletInfo");
         }
 
         byte app_major_version = appletInfo.getAppMajorVersion();
@@ -939,7 +932,7 @@ public class TPSProcessor {
         try {
             build_id = getAppletVersion();
         } catch (IOException e) {
-            CMS.debug("TPSProcessor.createTokenRecord: failed getting applet version:" + e + " ... continue");
+            CMS.debug(method + ": failed getting applet version:" + e + " ... continue");
         }
         if (build_id != null) {
             tokenRecord.setAppletID(Integer.toHexString(app_major_version) + "."
@@ -947,11 +940,211 @@ public class TPSProcessor {
                     build_id.toHexStringPlain());
         }
 
-        CMS.debug("TPSProcessor.createTokenRecord: ends");
+        CMS.debug(method + ": ends");
 
-        return tokenRecord;
     }
 
+    protected String getCAConnectorID() throws TPSException {
+        IConfigStore configStore = CMS.getConfigStore();
+        String id = null;
+
+        String config = "op." + currentTokenOperation + "." + selectedTokenType + ".ca.conn";
+
+        try {
+            id = configStore.getString(config, "ca1");
+        } catch (EBaseException e) {
+            throw new TPSException("TPSProcessor.getCAConnectorID: Internal error finding config value.");
+
+        }
+
+        CMS.debug("TPSProcessor.getCAConectorID: returning: " + id);
+
+        return id;
+    }
+
+    /*
+     * revokeCertificates revokes certificates on the token specified
+     * @param cuid the cuid of the token to revoke certificates
+     * @return auditMsg captures the audit message
+     * @throws TPSException in case of error
+     */
+    protected void revokeCertificates(String cuid) throws TPSException {
+        String auditMsg = "";
+        final String method = "TPSProcessor.revokeCertificates";
+
+        //bail out if not configured to revoke cert
+        IConfigStore configStore = CMS.getConfigStore();
+        String configName = TPSEngine.OP_FORMAT_PREFIX + "." + selectedTokenType + ".revokeCert";
+        boolean revokeCert = false;
+        try {
+            revokeCert = configStore.getBoolean(configName, false);
+        } catch (EBaseException e) {
+            auditMsg = method + ": config not found: "+ configName +
+                    "; default to false";
+            CMS.debug(auditMsg);
+            return;
+        }
+        if (!revokeCert) {
+            auditMsg = method + ":  revokeCert = false";
+            CMS.debug(auditMsg);
+            return;
+        }
+
+        if (cuid == null) {
+            auditMsg = "cuid null";
+            CMS.debug(method + ":" + auditMsg);
+            throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+        }
+        CMS.debug(method + ": begins for cuid:" + cuid);
+        TPSSubsystem tps = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+        boolean isTokenPresent = tps.tdb.isTokenPresent(cuid);
+        if (!isTokenPresent) {
+            auditMsg = method + ": token not found: "+ cuid;
+            CMS.debug(auditMsg);
+            throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+        }
+
+        String caConnId = getCAConnectorID();
+        CARemoteRequestHandler caRH = null;
+        try {
+            caRH = new CARemoteRequestHandler(caConnId);
+        } catch (EBaseException e) {
+            auditMsg = method + ": getting CARemoteRequestHandler failure";
+            CMS.debug(auditMsg);
+            throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+        }
+        //find all certs belonging to the token
+        Hashtable<String, TPSCertRecord> certRecords = tps.tdb.tdbGetCertificatesByCUID(cuid);
+
+        Set<String> keys = certRecords.keySet();
+        CMS.debug(method + ": found " + keys.size() + " certs");
+
+        for (String key: keys) {
+            TPSCertRecord cert = certRecords.get(key);
+            if (cert.getStatus().equals("revoked")) {
+                // already revoked cert should not be on token any more
+                CMS.debug(method + ": cert " + cert.getSerialNumber()
+                        + " already revoked; remove from tokendb and move on");
+                try {
+                    tps.certDatabase.removeRecord(cert.getId());
+                } catch (Exception e) {
+                    auditMsg = method + ": removeRecord failed";
+                    CMS.debug(auditMsg);
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+                }
+                continue;
+            }
+
+            String origin = cert.getOrigin();
+            if (origin!= null && !origin.equals(cuid)) {
+                /*
+                 * Raidzilla Bug #57803:
+                 * If the certificate is not originally created for this
+                 * token, we should not revoke the certificate here.
+                 * To figure out if this certificate is originally created
+                 * for this token, we check the tokenOrigin attribute.
+                 */
+                CMS.debug(method + ": cert " + cert.getSerialNumber()
+                        + " originally created for this token: "+ origin +
+                        " while current token: " + cuid
+                        + "; Remove from tokendb and skip the revoke");
+                try {
+                    tps.certDatabase.removeRecord(cert.getId());
+                } catch (Exception e) {
+                    auditMsg = method + ": removeRecord failed";
+                    CMS.debug(auditMsg);
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+                }
+                continue;
+            }
+            if (origin == null) {
+                // no tokenOrigin, then don't care, keep going
+                CMS.debug(method + ": tokenOrigin is not present in tokendb cert record");
+            }
+
+            // revoke the cert
+            /*
+             * if the certificates are revoked_on_hold, don't do anything because the certificates may
+             * be referenced by more than one token.
+             */
+            if (cert.getStatus().equals("revoked_on_hold")) {
+                CMS.debug(method + ": cert " + cert.getSerialNumber()
+                        + " has status revoked_on_hold; remove from tokendb and move on");
+                try {
+                    tps.certDatabase.removeRecord(cert.getId());
+                } catch (Exception e) {
+                    auditMsg = method + ": removeRecord failed";
+                    CMS.debug(auditMsg);
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+                }
+                continue;
+            }
+            configName = TPSEngine.OP_FORMAT_PREFIX + "." + selectedTokenType + ".revokeCert.revokeReason";
+            RevocationReason revokeReason = RevocationReason.UNSPECIFIED;
+            try {
+                int revokeReasonInt = configStore.getInteger(configName);
+                revokeReason = RevocationReason.fromInt(revokeReasonInt);
+            } catch (EBaseException e) {
+                auditMsg = method + ": config not found: " + configName +
+                        "; default to unspecified";
+                CMS.debug(auditMsg);
+                revokeReason = RevocationReason.UNSPECIFIED;
+            }
+            String hexSerial = cert.getSerialNumber();
+            if (hexSerial.length() >= 3 && hexSerial.startsWith("0x")) {
+                String serial = hexSerial.substring(2); // skip over the '0x'
+                BigInteger bInt = new BigInteger(serial, 16);
+                String serialStr = bInt.toString();
+                CMS.debug(method + ": found cert hex serial: " + serial +
+                        " dec serial:" + serialStr);
+                try {
+                    CARevokeCertResponse response =
+                            caRH.revokeCertificate(true, serialStr, cert.getCertificate(),
+                                    revokeReason);
+                    CMS.debug(method + ": response status =" + response.getStatus());
+                } catch (EBaseException e) {
+                    auditMsg = method + ": revokeCertificate from CA failed:" + e;
+                    CMS.debug(auditMsg);
+
+                    if (revokeReason == RevocationReason.CERTIFICATE_HOLD) {
+                        tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(),
+                                session.getIpAddress(), auditMsg,
+                                "failure");
+                    } else {
+                        tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(),
+                                session.getIpAddress(), auditMsg,
+                                "failure");
+                    }
+                    throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+                }
+            } else {
+                auditMsg = "mulformed hex serial number :" + hexSerial;
+                CMS.debug(method + ": " + auditMsg);
+                tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(), session.getIpAddress(),
+                        auditMsg,
+                        "failure");
+                throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_REVOKE_CERTIFICATES_FAILED);
+            }
+            auditMsg = "Certificate " + hexSerial + " revoked";
+            tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, session.getTokenRecord(), session.getIpAddress(), auditMsg,
+                    "success");
+
+            // delete cert from tokendb
+            CMS.debug(method + ": cert " + cert.getSerialNumber()
+                    + ": remove from tokendb");
+            try {
+                tps.certDatabase.removeRecord(cert.getId());
+            } catch (Exception e) {
+                auditMsg =  "removeRecord failed:" + e;
+                CMS.debug(method + ": " + auditMsg);
+                throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_UPDATE_TOKENDB_FAILED);
+            }
+            continue;
+        }
+        CMS.debug(method + ": done for cuid:" + cuid);
+    }
+
+
     protected void format(boolean skipAuth) throws TPSException, IOException {
 
         String auditMsg = null;
@@ -966,14 +1159,14 @@ public class TPSProcessor {
             appletInfo = getAppletInfo();
         } catch (TPSException e) {
             auditMsg = e.toString();
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                    auditMsg, "failure");
+            tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg,
+                    "failure");
 
             throw e;
         }
         appletInfo.setAid(getCardManagerAID());
         boolean isTokenPresent = false;
-        tokenRecord = createTokenRecord(isTokenPresent, appletInfo);
+        fillTokenRecord(tokenRecord, appletInfo);
         session.setTokenRecord(tokenRecord);
 
         String cuid = appletInfo.getCUIDhexString();
@@ -1008,8 +1201,8 @@ public class TPSProcessor {
                 tokenType = resolveTokenProfile(resolverInstName, cuid, msn, major_version, minor_version);
             } catch (TPSException e) {
                 auditMsg = e.toString();
-                tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                        auditMsg, "failure");
+                tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg,
+                        "failure");
 
                 throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_MISCONFIGURATION);
             }
@@ -1028,8 +1221,8 @@ public class TPSProcessor {
             } catch (EBaseException e) {
                 CMS.debug("TPSProcessor.format: Internal Error obtaining mandatory config values. Error: " + e);
                 auditMsg = "TPS error getting config values from config store." + e.toString();
-                tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                        auditMsg, "failure");
+                tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg,
+                        "failure");
 
                 throw new TPSException(auditMsg, TPSStatus.STATUS_ERROR_MISCONFIGURATION);
             }
@@ -1053,8 +1246,8 @@ public class TPSProcessor {
                     CMS.debug("TPSProcessor.format:: authentication exception thrown: " + e);
                     auditMsg = "authentication failed, status = STATUS_ERROR_LOGIN";
 
-                    tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                            auditMsg, "failure");
+                    tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg,
+                            "failure");
 
                     throw new TPSException(auditMsg,
                             TPSStatus.STATUS_ERROR_LOGIN);
@@ -1083,8 +1276,8 @@ public class TPSProcessor {
                         " Disabled, illegal transition attempted " + tokenRecord.getTokenStatus() +
                         " to " + newState;
 
-                tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                        auditMsg, "failure");
+                tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg,
+                        "failure");
 
                 throw new TPSException(auditMsg,
                         TPSStatus.STATUS_ERROR_DISABLED_TOKEN);
@@ -1137,25 +1330,34 @@ public class TPSProcessor {
         channel.externalAuthenticate();
         tokenRecord.setKeyInfo(channel.getKeyInfoData().toHexStringPlain());
 
+       if (isTokenPresent) {
+            // Revoke certificates on token, if so configured
+            try {
+                revokeCertificates(tokenRecord.getId());
+            } catch (TPSException te) {
+                // failed revocation; capture message and continue
+                auditMsg = te.getMessage();
+            }
+        }
+
         // Update Token DB
         try {
-            tps.tdb.tdbUpdateTokenEntry(tps, tokenRecord);
+            tps.tdb.tdbUpdateTokenEntry(tokenRecord);
             String successMsg = "update token success";
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                    successMsg, "success");
+            tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), successMsg,
+                    "success");
         } catch (Exception e) {
             String failMsg = "update token failure";
             auditMsg = failMsg + ":" + e.toString();
-            tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(),
-                    failMsg, "failure");
+            tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), failMsg,
+                    "failure");
 
             throw new TPSException(auditMsg);
         }
 
-        // ToDo:  Revoke certificates
         auditMsg = "format operation succeeded";
 
-        tps.tdb.tdbActivity(tps, ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, "success");
+        tps.tdb.tdbActivity(ActivityDatabase.OP_FORMAT, tokenRecord, session.getIpAddress(), auditMsg, "success");
 
         CMS.debug("TPSProcessor.format:: ends");
 
@@ -1229,7 +1431,6 @@ public class TPSProcessor {
 
         CMS.debug("TPSProcessor.getResolverInstanceName: returning: " + resolverInstName);
 
-        // TODO Auto-generated method stub
         return resolverInstName;
     }
 
@@ -2106,10 +2307,10 @@ public class TPSProcessor {
             String key = entry.getKey();
 
             String value = entry.getValue();
-            CMS.debug("TPSEnrollProcessor.mapPattern: Exposed: key: " + key + " Param: " + value);
+            CMS.debug("TPSProcessor.mapPattern: Exposed: key: " + key + " Param: " + value);
 
             if (key.equals(patternToMap)) {
-                CMS.debug("TPSEnrollProcessor.mapPattern: found match: key: " + key + " mapped to: " + value);
+                CMS.debug("TPSProcessor.mapPattern: found match: key: " + key + " mapped to: " + value);
                 patternMapped = value;
                 break;
             }
@@ -2118,7 +2319,7 @@ public class TPSProcessor {
 
         result = piece1 + patternMapped + piece2;
 
-        CMS.debug("TPSEnrollProcessor.mapPattern: returning: " + result);
+        CMS.debug("TPSProcessor.mapPattern: returning: " + result);
         return result;
 
     }
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/rest/TokenService.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/rest/TokenService.java
index 84046a964375cc7f9884249d311bd099c59008d7..898091d20b26451ed84db6405b5f4612dfe345e4 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/rest/TokenService.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/rest/TokenService.java
@@ -292,18 +292,16 @@ public class TokenService extends PKIService implements TokenResource {
             tokenRecord = createTokenRecord(tokenData);
             tokenRecord.setId(tokenID);
             database.addRecord(tokenID, tokenRecord);
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_ADD,
-                tokenRecord, ipAddress, msg, "success",
-                remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_ADD, tokenRecord,
+                ipAddress, msg, "success", remoteUser);
             tokenData = createTokenData(database.getRecord(tokenID));
 
             return createCreatedResponse(tokenData, tokenData.getLink().getHref());
 
         } catch (Exception e) {
             e.printStackTrace();
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_ADD,
-                tokenRecord, ipAddress, msg, "failure",
-                remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_ADD, tokenRecord,
+                ipAddress, msg, "failure", remoteUser);
             msg = msg + ":" + e;
 
             throw new PKIException(msg);
@@ -334,9 +332,8 @@ public class TokenService extends PKIService implements TokenResource {
             tokenRecord.setKeyInfo(tokenData.getKeyInfo());
             tokenRecord.setPolicy(tokenData.getPolicy());
             database.updateRecord(tokenID, tokenRecord);
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                tokenRecord, ipAddress, msg, "success",
-                remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                ipAddress, msg, "success", remoteUser);
 
             tokenData = createTokenData(database.getRecord(tokenID));
 
@@ -344,9 +341,9 @@ public class TokenService extends PKIService implements TokenResource {
 
         } catch (Exception e) {
             e.printStackTrace();
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                tokenRecord, ipAddress, msg,
-                "failure", remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                ipAddress, msg, "failure",
+                remoteUser);
             msg = msg + ":" + e;
 
             throw new PKIException(msg);
@@ -403,9 +400,8 @@ public class TokenService extends PKIService implements TokenResource {
             }
 
             database.updateRecord(tokenID, tokenRecord);
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                tokenRecord, ipAddress, msg, "success",
-                remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                ipAddress, msg, "success", remoteUser);
 
             tokenData = createTokenData(database.getRecord(tokenID));
 
@@ -413,9 +409,9 @@ public class TokenService extends PKIService implements TokenResource {
 
         } catch (Exception e) {
             e.printStackTrace();
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                tokenRecord, ipAddress, msg,
-                "failure", remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                ipAddress, msg, "failure",
+                remoteUser);
             msg = msg + ":" + e;
 
             throw new PKIException(msg);
@@ -450,19 +446,19 @@ public class TokenService extends PKIService implements TokenResource {
             if (nextStatuses == null || !nextStatuses.contains(tokenStatus)) {
                 CMS.debug("TokenService.changeTokenStatus(): next status not allowed: " + tokenStatus);
                 msg = msg + ": Invalid token status transition";
-                subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                    tokenRecord, ipAddress,
-                    msg,
-                    "failure", remoteUser);
+                subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                    ipAddress, msg,
+                    "failure",
+                    remoteUser);
                 throw new BadRequestException(msg);
             }
 
             CMS.debug("TokenService.changeTokenStatus(): next status allowed: " + tokenStatus);
             setTokenStatus(tokenRecord, tokenStatus);
             database.updateRecord(tokenID, tokenRecord);
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                tokenRecord, ipAddress, msg,
-                "success", remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                ipAddress, msg, "success",
+                remoteUser);
 
             TokenData tokenData = createTokenData(database.getRecord(tokenID));
 
@@ -471,9 +467,9 @@ public class TokenService extends PKIService implements TokenResource {
         } catch (Exception e) {
             e.printStackTrace();
             msg = msg + e;
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DO_TOKEN,
-                tokenRecord, ipAddress, msg,
-                "failure", remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DO_TOKEN, tokenRecord,
+                ipAddress, msg, "failure",
+                remoteUser);
 
             throw new PKIException(msg);
         }
@@ -496,17 +492,16 @@ public class TokenService extends PKIService implements TokenResource {
             TokenDatabase database = subsystem.getTokenDatabase();
             tokenRecord = database.getRecord(tokenID);
             database.removeRecord(tokenID);
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DELETE,
-                tokenRecord, ipAddress, msg, "success",
-                remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DELETE, tokenRecord,
+                ipAddress, msg, "success", remoteUser);
 
             return createNoContentResponse();
 
         } catch (Exception e) {
             e.printStackTrace();
-            subsystem.tdb.tdbActivity(subsystem, ActivityDatabase.OP_DELETE,
-                tokenRecord, ipAddress, msg,
-                "failure", remoteUser);
+            subsystem.tdb.tdbActivity(ActivityDatabase.OP_DELETE, tokenRecord,
+                ipAddress, msg, "failure",
+                remoteUser);
             msg = msg + ":" + e;
 
             throw new PKIException(msg);
-- 
1.8.4.2

