From 322fc30f6bd5f8a7d419a865bf6ad6b7c64de979 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Sat, 18 Feb 2017 12:27:49 -0800
Subject: [PATCH] Bug 1419734 CMC: id-cmc-identityProofV2 feature
 implementation This patch adds both client and server support for two cmc
 controls: id-cmc-identityProofV2 - for supporting RFC5272, and
 id-cmc-identification - for assisting in shared secret search; Note: for
 client, only CMCRequest is updated in this patch

---
 .../certsrv/authentication/ISharedToken.java       |   3 +
 .../src/com/netscape/cmstools/CMCRequest.java      | 184 +++++++++++++++-
 .../netscape/cms/authentication/SharedSecret.java  |   5 +
 .../netscape/cms/profile/common/EnrollProfile.java | 237 ++++++++++++++++++---
 4 files changed, 468 insertions(+), 38 deletions(-)

diff --git a/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java b/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java
index 830c8866e82471f7b97888bb77ebf5d1f64cf858..84b02440416d84662555751d2485b1df83305659 100644
--- a/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java
+++ b/base/common/src/com/netscape/certsrv/authentication/ISharedToken.java
@@ -26,6 +26,9 @@ import org.mozilla.jss.pkix.cmc.PKIData;
  */
 public interface ISharedToken {
 
+    // support for id_cmc_identification
+    public String getSharedToken(String identification);
+
     public String getSharedToken(PKIData cmcData);
 
     public String getSharedToken(BigInteger serialnum);
diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
index c518082123f96a30ac0b6fbdec7174bd723ba322..cf2f66d4fbac555419f2e4f1cfc0b49ff22d6322 100644
--- a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
+++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
@@ -21,6 +21,7 @@ import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.CharConversionException;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -54,6 +55,7 @@ import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.pkcs10.CertificationRequest;
 import org.mozilla.jss.pkix.cmc.CMCCertId;
 import org.mozilla.jss.pkix.cmc.GetCert;
+import org.mozilla.jss.pkix.cmc.IdentityProofV2;
 import org.mozilla.jss.pkix.cmc.LraPopWitness;
 import org.mozilla.jss.pkix.cmc.OtherMsg;
 import org.mozilla.jss.pkix.cmc.PKIData;
@@ -144,7 +146,11 @@ public class CMCRequest {
      */
     static ContentInfo getCMCBlob(X509Certificate signerCert, String tokenName, String nickname,
             String[] rValue, String format, CryptoManager manager, String transactionMgtEnable,
-            String transactionMgtId, String identityProofEnable, String identityProofSharedSecret,
+            String transactionMgtId,
+            String identificationEnable, String identification,
+            String identityProofEnable, String identityProofSharedSecret,
+            String identityProofV2Enable, String identityProofV2SharedSecret,
+            String identityProofV2hashAlg, String identityProofV2macAlg,
             SEQUENCE controlSeq, SEQUENCE otherMsgSeq, int bpid) {
 
         System.out.println("in getCMCBlob");
@@ -228,9 +234,21 @@ public class CMCRequest {
                 bpid = addTransactionAttr(bpid, controlSeq, transactionMgtId, format,
                         pkcs, certReqMsg);
 
-            if (identityProofEnable.equals("true"))
+            if (identificationEnable.equals("true")) {
+                bpid = addIdentificationAttr(bpid, controlSeq, identification);
+            }
+
+            // for identityProof, it's either V2 or not V2; can't be both
+            // if both, V2 takes precedence
+            if (identityProofV2Enable.equals("true")) {
+                bpid = addIdentityProofV2Attr(bpid, controlSeq, reqSequence,
+                        identityProofV2SharedSecret,
+                        (identificationEnable.equals("true")) ? identification : null,
+                        identityProofV2hashAlg, identityProofV2macAlg);
+            } else if (identityProofV2Enable.equals("true")) {
                 bpid = addIdentityProofAttr(bpid, controlSeq, reqSequence,
                         identityProofSharedSecret);
+            }
 
             PKIData pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq);
 
@@ -416,9 +434,29 @@ public class CMCRequest {
         System.out.println("#                                  is present.");
         System.out.println("revRequest.invalidityDatePresent=false");
         System.out.println("");
+        System.out.println("#identityProofV2.enable: if true, then the request will contain");
+        System.out.println("#this control. Otherwise, false.");
+        System.out.println("#Note that if both identityProof and identityProofV2");
+        System.out.println("#  are enabled, identityProofV2 takes precedence; Only one of them can be active at a time");
+        System.out.println("#Supported hashAlg are:");
+        System.out.println("# SHA-1, SHA-256, SHA-384, and SHA-512");
+        System.out.println("#Supported macAlg are:");
+        System.out.println("# SHA-1-HMAC, SHA-256-HMAC, SHA-384-HMAC, and SHA-512-HMAC");
+        System.out.println("identityProofV2.enable=false");
+        System.out.println("identityProofV2.hashAlg=SHA-256");
+        System.out.println("identityProofV2.macAlg=SHA-256-HMAC");
+        System.out.println("");
+        System.out.println("#identityProofV2.sharedSecret: Shared Secret");
+        System.out.println("identityProofV2.sharedSecret=testing");
+        System.out.println("");
+        System.out.println("#identification works with identityProofV2");
+        System.out.println("identification.enable=false");
+        System.out.println("identification=testuser");
+        System.out.println("");
         System.out.println("#identityProof.enable: if true, then the request will contain");
         System.out.println("#this control. Otherwise, false.");
-        System.out.println("identityProof.enable=true");
+        System.out.println("#Note that this control is updated by identityProofV2 above");
+        System.out.println("identityProof.enable=false");
         System.out.println("");
         System.out.println("#identityProof.sharedSecret: Shared Secret");
         System.out.println("identityProof.sharedSecret=testing");
@@ -503,6 +541,90 @@ public class CMCRequest {
         return RevRequest.unspecified;
     }
 
+    /**
+     * add IdentityProofV2 to the control sequence
+     *
+     * @param bpid Body part id
+     * @param seq control sequence
+     * @param reqSequence request sequence
+     * @param sharedSecret shared secret
+     * @param hashAlgString hash algorithm
+     * @param macAlgString mac algorithm
+     * cfu
+     */
+    private static int addIdentityProofV2Attr(int bpid,
+            SEQUENCE seq, SEQUENCE reqSequence,
+            String sharedSecret,
+            String ident,
+            String hashAlgString, String macAlgString) {
+        String method = "CMCRequest: addIdentityProofV2Attr: ";
+        byte[] b = ASN1Util.encode(reqSequence);
+        byte[] key = null;
+        byte[] finalDigest = null;
+
+        // default to SHA256 if not specified
+        if (hashAlgString == null) {
+            hashAlgString = "SHA-256";
+        }
+        if (macAlgString == null) {
+            macAlgString = "SHA-256-HMAC";
+        }
+        System.out.println(method + "hashAlg=" + hashAlgString +
+                "; macAlg=" + macAlgString);
+
+        String toBeDigested = sharedSecret;
+        if (ident != null) {
+            toBeDigested = sharedSecret + ident;
+        }
+        try {
+            MessageDigest hash = MessageDigest.getInstance(hashAlgString);
+            key = hash.digest(toBeDigested.getBytes());
+        } catch (NoSuchAlgorithmException ex) {
+            System.out.println(method + "No such algorithm!");
+            return -1;
+        }
+
+        MessageDigest mac;
+        try {
+            mac = MessageDigest.getInstance(CryptoUtil.getHMACtoMessageDigestName(macAlgString));
+            HMACDigest hmacDigest = new HMACDigest(mac, key);
+            hmacDigest.update(b);
+            finalDigest = hmacDigest.digest();
+        } catch (NoSuchAlgorithmException ex) {
+            System.out.println(method + "No such algorithm!");
+            return -1;
+        }
+
+        AlgorithmIdentifier hashAlg;
+        try {
+            hashAlg = new AlgorithmIdentifier(CryptoUtil.getHashAlgorithmOID(hashAlgString));
+        } catch (NoSuchAlgorithmException ex) {
+            System.out.println(method + "No such hashing algorithm:" + hashAlgString);
+            return -1;
+        }
+        AlgorithmIdentifier macAlg;
+        try {
+            macAlg = new AlgorithmIdentifier(CryptoUtil.getHMACAlgorithmOID(macAlgString));
+        } catch (NoSuchAlgorithmException ex) {
+            System.out.println(method + "No such HMAC algorithm:" + macAlgString);
+            return -1;
+        }
+        IdentityProofV2 idV2val = new IdentityProofV2(hashAlg, macAlg, new OCTET_STRING(finalDigest));
+        TaggedAttribute identityProofV2 = new TaggedAttribute(new INTEGER(bpid++),
+                OBJECT_IDENTIFIER.id_cmc_identityProofV2,
+                idV2val);
+        seq.addElement(identityProofV2);
+        System.out.println("Identity Proof V2 control: ");
+        System.out.print("   Value: ");
+        for (int i = 0; i < finalDigest.length; i++) {
+            System.out.print(finalDigest[i] + " ");
+        }
+        System.out.println("");
+        System.out.println("Successfully create identityProofV2 control. bpid = " + (bpid - 1));
+        System.out.println("");
+        return bpid;
+    }
+
     private static int addIdentityProofAttr(int bpid, SEQUENCE seq, SEQUENCE reqSequence,
             String sharedSecret) {
         byte[] b = ASN1Util.encode(reqSequence);
@@ -797,6 +919,40 @@ public class CMCRequest {
         return bpid;
     }
 
+    /**
+     * addIdentificationAttr adds the identification control
+     *
+     * @param bpid
+     * @param seq
+     * @param ident
+     * @return
+     * cfu
+     */
+    private static int addIdentificationAttr(int bpid, SEQUENCE seq, String ident) {
+        UTF8String ident_s = null;
+        if (ident == null) {
+            System.out.println("Error in creating identification control: identification null");
+            System.exit(1);
+        } else {
+            System.out.println("identification control: identification =" + ident);
+        }
+
+        try {
+            if (ident.length() > 0)
+                ident_s = new UTF8String(ident);
+        } catch (CharConversionException e) {
+            System.out.println("Error in creating identification control:" + e.toString());
+            System.exit(1);
+        }
+
+        TaggedAttribute identVal = new TaggedAttribute(new INTEGER(bpid++), OBJECT_IDENTIFIER.id_cmc_identification,
+                ident_s);
+        System.out.println("Successfully create identification control. bpid = " + (bpid - 1));
+        System.out.println("");
+        seq.addElement(identVal);
+        return bpid;
+    }
+
     private static int addPopLinkWitnessAttr(int bpid, SEQUENCE controlSeq) {
         byte[] seed =
         { 0x10, 0x53, 0x42, 0x24, 0x1a, 0x2a, 0x35, 0x3c,
@@ -831,7 +987,9 @@ public class CMCRequest {
         String revRequestEnable = "false", revRequestIssuer = null, revRequestSerial = null;
         String revRequestReason = null, revRequestSharedSecret = null, revRequestComment = null;
         String revRequestInvalidityDatePresent = "false";
+        String identificationEnable = "false", identification = null;
         String identityProofEnable = "false", identityProofSharedSecret = null;
+        String identityProofV2Enable = "false", identityProofV2SharedSecret = null, identityProofV2hashAlg = "SHA256", identityProofV2macAlg = "SHA256";
         String popLinkWitnessEnable = "false";
         String bodyPartIDs = null, lraPopWitnessEnable = "false";
 
@@ -928,6 +1086,18 @@ public class CMCRequest {
                         revRequestInvalidityDatePresent = val;
                     } else if (name.equals("revRequest.nickname")) {
                         revCertNickname = val;
+                    } else if (name.equals("identification.enable")) {
+                        identificationEnable = val;
+                    } else if (name.equals("identification")) {
+                        identification = val;
+                    } else if (name.equals("identityProofV2.enable")) {
+                        identityProofV2Enable = val;
+                    } else if (name.equals("identityProofV2.sharedSecret")) {
+                        identityProofV2SharedSecret = val;
+                    } else if (name.equals("identityProofV2.hashAlg")) {
+                        identityProofV2hashAlg = val;
+                    } else if (name.equals("identityProofV2.macAlg")) {
+                        identityProofV2macAlg = val;
                     } else if (name.equals("identityProof.enable")) {
                         identityProofEnable = val;
                     } else if (name.equals("identityProof.sharedSecret")) {
@@ -1148,8 +1318,12 @@ public class CMCRequest {
             }
 
             ContentInfo cmcblob = getCMCBlob(signerCert, tokenName, nickname, requests, format,
-                    cm, transactionMgtEnable, transactionMgtId, identityProofEnable,
-                    identityProofSharedSecret, controlSeq, otherMsgSeq, bpid);
+                    cm, transactionMgtEnable, transactionMgtId,
+                    identificationEnable, identification,
+                    identityProofEnable, identityProofSharedSecret,
+                    identityProofV2Enable, identityProofV2SharedSecret,
+                    identityProofV2hashAlg, identityProofV2macAlg,
+                    controlSeq, otherMsgSeq, bpid);
 
             // (6) Finally, print the actual CMC blob to the
             //     specified output file
diff --git a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
index 7a0784c53c95af27ac38bafa59d3db26f336fd1e..eac5a082a78ed38873f9811e27f27dea39c706d7 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
@@ -28,6 +28,11 @@ public class SharedSecret implements ISharedToken {
     public SharedSecret() {
     }
 
+    // support for id_cmc_identification
+    public String getSharedToken(String identification) {
+        return "testing";
+    }
+
     public String getSharedToken(PKIData cmcdata) {
         return "testing";
     }
diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
index 8d10ec26b3db12f68eb9033473b93615d5a6d824..b93072dc73f6929b078bc6cb020ebb19be146aa4 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
@@ -39,9 +39,13 @@ import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
 import org.mozilla.jss.asn1.OCTET_STRING;
 import org.mozilla.jss.asn1.SEQUENCE;
 import org.mozilla.jss.asn1.SET;
+import org.mozilla.jss.asn1.UTF8String;
 import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.DigestAlgorithm;
+import org.mozilla.jss.crypto.HMACAlgorithm;
 import org.mozilla.jss.pkcs10.CertificationRequest;
 import org.mozilla.jss.pkcs10.CertificationRequestInfo;
+import org.mozilla.jss.pkix.cmc.IdentityProofV2;
 import org.mozilla.jss.pkix.cmc.LraPopWitness;
 import org.mozilla.jss.pkix.cmc.OtherMsg;
 import org.mozilla.jss.pkix.cmc.PKIData;
@@ -412,28 +416,80 @@ public abstract class EnrollProfile extends BasicProfile
                 if (numcontrols > 0) {
                     context.put("numOfControls", Integer.valueOf(numcontrols));
                     TaggedAttribute[] attributes = new TaggedAttribute[numcontrols];
+
+                    boolean id_cmc_identification = false;
+                    SET ident = null;
+
+                    boolean id_cmc_identityProofV2 = false;
+                    boolean id_cmc_identityProof = false;
+                    TaggedAttribute attr = null;
+
+                    boolean id_cmc_idPOPLinkRandom = false;
+                    SET vals = null;
+
                     for (int i = 0; i < numcontrols; i++) {
                         attributes[i] = (TaggedAttribute) controlSeq.elementAt(i);
                         OBJECT_IDENTIFIER oid = attributes[i].getType();
-                        if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProof)) {
-                            boolean valid = verifyIdentityProof(attributes[i],
-                                    reqSeq);
-                            if (!valid) {
-                                SEQUENCE bpids = getRequestBpids(reqSeq);
-                                context.put("identityProof", bpids);
-                                return null;
-                            }
+                        if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) {
+                            id_cmc_identification = true;
+                            ident = attributes[i].getValues();
+                        } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProofV2)) {
+                            id_cmc_identityProofV2 = true;
+                            attr = attributes[i];
+                        } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProof)) {
+                            id_cmc_identityProof = true;
+                            attr = attributes[i];
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_idPOPLinkRandom)) {
-                            SET vals = attributes[i].getValues();
-                            OCTET_STRING ostr =
-                                    (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
-                                            ASN1Util.encode(vals.elementAt(0))));
-                            randomSeed = ostr.toByteArray();
+                            id_cmc_idPOPLinkRandom = true;
+                            vals = attributes[i].getValues();
                         } else {
                             context.put(attributes[i].getType(), attributes[i]);
                         }
+                    } //for
+
+                    /**
+                     * TODO: cfu: phase 2 should add enforcement for
+                     * id_cmc_identityProofV2 and id_cmc_identification control
+                     * when needed
+                     */
+
+                    /**
+                     * now do the actual control processing
+                     * (the postponed processing is so that we can capture
+                     * the identification, if included)
+                     */
+
+                    UTF8String ident_s = null;
+                    if (id_cmc_identification) {
+                        ident_s = (UTF8String) (ASN1Util.decode(UTF8String.getTemplate(),
+                                ASN1Util.encode(ident.elementAt(0))));
                     }
-                }
+
+                    // either V2 or not V2; can't be both
+                    if (id_cmc_identityProofV2 && (attr != null)) {
+                        boolean valid = verifyIdentityProofV2(attr, ident_s,
+                                reqSeq);
+                        if (!valid) {
+                            SEQUENCE bpids = getRequestBpids(reqSeq);
+                            context.put("identityProofV2", bpids);
+                            return null;
+                        }
+                    } else if (id_cmc_identityProof && (attr != null)) {
+                        boolean valid = verifyIdentityProof(attr,
+                                reqSeq);
+                        if (!valid) {
+                            SEQUENCE bpids = getRequestBpids(reqSeq);
+                            context.put("identityProof", bpids);
+                            return null;
+                        }
+                    }
+
+                    if (id_cmc_idPOPLinkRandom && vals != null) {
+                        OCTET_STRING ostr = (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
+                                ASN1Util.encode(vals.elementAt(0))));
+                        randomSeed = ostr.toByteArray();
+                    }
+                } // numcontrols > 0
             }
 
             SEQUENCE otherMsgSeq = pkiData.getOtherMsgSequence();
@@ -580,39 +636,54 @@ public abstract class EnrollProfile extends BasicProfile
     }
 
     private boolean verifyDigest(byte[] sharedSecret, byte[] text, byte[] bv) {
+        MessageDigest hashAlg;
+        try {
+            hashAlg = MessageDigest.getInstance("SHA1");
+        } catch (NoSuchAlgorithmException ex) {
+            CMS.debug("EnrollProfile:verifyDigest: " + ex.toString());
+            return false;
+        }
+
+        return verifyDigest(sharedSecret, text, bv, hashAlg, hashAlg);
+    }
+
+    /**
+     * verifyDigest verifies digest using the
+     * specified hashAlg and macAlg
+     *
+     * @param sharedSecret shared secret in bytes
+     * @param text data to be verified in bytes
+     * @param bv witness in bytes
+     * @param hashAlg hashing algorithm
+     * @param macAlg message authentication algorithm
+     * cfu
+     */
+    private boolean verifyDigest(byte[] sharedSecret, byte[] text, byte[] bv,
+            MessageDigest hashAlg, MessageDigest macAlg) {
+        String method = "EnrollProfile:verifyDigest: ";
         byte[] key = null;
-        try {
-            MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1");
-            key = SHA1Digest.digest(sharedSecret);
-        } catch (NoSuchAlgorithmException ex) {
-            CMS.debug("EnrollProfile: No such algorithm for this message digest.");
-            return false;
-        }
+        CMS.debug(method + "in verifyDigest: hashAlg=" + hashAlg.toString() +
+                "; macAlg=" + macAlg.toString());
+        key = hashAlg.digest(sharedSecret);
 
         byte[] finalDigest = null;
-        try {
-            MessageDigest SHA1Digest = MessageDigest.getInstance("SHA1");
-            HMACDigest hmacDigest = new HMACDigest(SHA1Digest, key);
-            hmacDigest.update(text);
-            finalDigest = hmacDigest.digest();
-        } catch (NoSuchAlgorithmException ex) {
-            CMS.debug("EnrollProfile: No such algorithm for this message digest.");
-            return false;
-        }
+        HMACDigest hmacDigest = new HMACDigest(macAlg, key);
+        hmacDigest.update(text);
+        finalDigest = hmacDigest.digest();
 
         if (finalDigest.length != bv.length) {
-            CMS.debug("EnrollProfile: The length of two HMAC digest are not the same.");
+            CMS.debug(method + " The length of two HMAC digest are not the same.");
             return false;
         }
 
         for (int j = 0; j < bv.length; j++) {
             if (bv[j] != finalDigest[j]) {
-                CMS.debug("EnrollProfile: The content of two HMAC digest are not the same.");
+                CMS.debug(method + " The content of two HMAC digest are not the same.");
                 return false;
             }
         }
 
-        CMS.debug("EnrollProfile: The content of two HMAC digest are the same.");
+        CMS.debug(method + " The content of two HMAC digest are the same.");
         return true;
     }
 
@@ -633,6 +704,106 @@ public abstract class EnrollProfile extends BasicProfile
         return bpids;
     }
 
+    /**
+     * verifyIdentityProofV2 handles IdentityProofV2 as defined by RFC5272
+     *
+     * @param attr controlSequence of the PKI request PKIData
+     * @param ident value of the id_cmc_identification control
+     * @param reqSeq requestSequence of the PKI request PKIData
+     * @return boolean true if the witness values correctly verified
+     * cfu
+     */
+    private boolean verifyIdentityProofV2(
+            TaggedAttribute attr,
+            UTF8String ident,
+            SEQUENCE reqSeq) {
+        String method = "EnrollProfile:verifyIdentityProofV2: ";
+        CMS.debug(method + " begins");
+
+        String ident_string = null;
+        if (ident != null) {
+            ident_string = ident.toString();
+            // cfu: REMOVE
+            CMS.debug(method + "received ident String: " + ident_string);
+        }
+
+        SET vals = attr.getValues(); // getting the IdentityProofV2 structure
+        if (vals.size() < 1) {
+            return false;
+        }
+
+        String name = null;
+        try {
+            String configName = "cmc.sharedSecret.class";
+            CMS.debug(method + "getting :" + configName);
+            name = CMS.getConfigStore().getString(configName);
+            CMS.debug(method + "Shared Secret plugin class name retrieved:" +
+                    name);
+        } catch (Exception e) {
+            CMS.debug(method + " Failed to retrieve shared secret plugin class name");
+            return false;
+        }
+
+        ISharedToken tokenClass = null;
+        try {
+            tokenClass = (ISharedToken) Class.forName(name).newInstance();
+            CMS.debug(method + "Shared Secret plugin class retrieved");
+        } catch (ClassNotFoundException e) {
+            CMS.debug(method + " Failed to find class name: " + name);
+            return false;
+        } catch (InstantiationException e) {
+            CMS.debug("EnrollProfile: Failed to instantiate class: " + name);
+            return false;
+        } catch (IllegalAccessException e) {
+            CMS.debug(method + " Illegal access: " + name);
+            return false;
+        }
+
+        String token = null;
+        if (ident_string != null)
+            token = tokenClass.getSharedToken(ident_string);
+        else
+            token = tokenClass.getSharedToken(mCMCData);
+
+        if (token == null) {
+            CMS.debug(method + " Failed to retrieve shared secret");
+            return false;
+        }
+        // cfu REMOVE
+        CMS.debug(method + "Shared Secret returned by tokenClass:" + token);
+        try {
+            IdentityProofV2 idV2val = (IdentityProofV2) (ASN1Util.decode(IdentityProofV2.getTemplate(),
+                    ASN1Util.encode(vals.elementAt(0))));
+            /**
+             * TODO: cfu:
+             * phase2: getting configurable allowable hashing and mac algorithms
+             */
+
+            DigestAlgorithm hashAlgID = DigestAlgorithm.fromOID(idV2val.getHashAlgID().getOID());
+            MessageDigest hashAlg = MessageDigest.getInstance(hashAlgID.toString());
+            // TODO: check against CA allowed algs later
+
+            HMACAlgorithm macAlgId = HMACAlgorithm.fromOID(idV2val.getMacAlgId().getOID());
+            MessageDigest macAlg = MessageDigest
+                    .getInstance(CryptoUtil.getHMACtoMessageDigestName(macAlgId.toString()));
+            // TODO: check against CA allowed algs later
+
+            OCTET_STRING witness = idV2val.getWitness();
+
+            byte[] witness_bytes = witness.toByteArray();
+            byte[] request_bytes = ASN1Util.encode(reqSeq); // PKIData reqSequence field
+            return verifyDigest(
+                    (ident_string != null) ? (token + ident_string).getBytes() : token.getBytes(),
+                    request_bytes,
+                    witness_bytes,
+                    hashAlg, macAlg);
+        } catch (Exception e) {
+            CMS.debug(method + " Failed with Exception: " + e.toString());
+            return false;
+        }
+
+    } // verifyIdentityProofV2
+
     private boolean verifyIdentityProof(TaggedAttribute attr, SEQUENCE reqSeq) {
         SET vals = attr.getValues();
         if (vals.size() < 1)
