Blob Blame History Raw
From 724b91a4688ca73a07c6c225e0e61e0a79073f42 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Fri, 16 Jun 2017 18:20:38 -0700
Subject: [PATCH] Ticket #2616 CMC: id-cmc-statusInfo ==> id-cmc-statusInfoV2

This patch contains the following update:
* Structurely, CMCStatusInfo to CMCStatusInfoV2 update; no extendedFailInfo has been added at this point
* In case of EncryptedPOP, instead of returning with CMCStatus pending where
  PendInfo contains the requestID, it now returns CMCStatus failed whith
  responseInfo control contains the requestID. On the client side, CMCRequest
  now processes the responseInfo and returns the DecryptedPOP with requestID in
  the regInfo control. CMCResponse has been updated to handle the new controls
  as well.
* A number of fail info codes are now being supported by the server to add
  clarity to CMC failed status, including:
  badMessageCheck, badRequest, unsuportedExt, badIdentity, popRequired, and popFailed.

(cherry picked from commit 6273907e0ca36425fa30c106b7fdd28c510b1162)
---
 .../certsrv/profile/ECMCBadIdentityException.java  |  54 ++++++
 .../profile/ECMCBadMessageCheckException.java      |  54 ++++++
 .../certsrv/profile/ECMCBadRequestException.java   |  53 +++++
 .../certsrv/profile/ECMCPopFailedException.java    |  53 +++++
 .../certsrv/profile/ECMCPopRequiredException.java  |  59 ++++++
 .../profile/ECMCUnsupportedExtException.java       |  53 +++++
 .../src/com/netscape/cmstools/CMCRequest.java      |  69 +++++--
 .../src/com/netscape/cmstools/CMCResponse.java     |  36 +++-
 .../netscape/cms/profile/common/EnrollProfile.java | 195 ++++++++++++-------
 .../cms/servlet/common/CMCOutputTemplate.java      | 216 ++++++++++++---------
 .../servlet/common/GenPendingTemplateFiller.java   |  10 +-
 .../servlet/profile/ProfileSubmitCMCServlet.java   |  96 ++++++++-
 .../netscape/cms/servlet/request/CheckRequest.java |   8 +-
 base/server/cmsbundle/src/UserMessages.properties  |   1 +
 14 files changed, 747 insertions(+), 210 deletions(-)
 create mode 100644 base/common/src/com/netscape/certsrv/profile/ECMCBadIdentityException.java
 create mode 100644 base/common/src/com/netscape/certsrv/profile/ECMCBadMessageCheckException.java
 create mode 100644 base/common/src/com/netscape/certsrv/profile/ECMCBadRequestException.java
 create mode 100644 base/common/src/com/netscape/certsrv/profile/ECMCPopFailedException.java
 create mode 100644 base/common/src/com/netscape/certsrv/profile/ECMCPopRequiredException.java
 create mode 100644 base/common/src/com/netscape/certsrv/profile/ECMCUnsupportedExtException.java

diff --git a/base/common/src/com/netscape/certsrv/profile/ECMCBadIdentityException.java b/base/common/src/com/netscape/certsrv/profile/ECMCBadIdentityException.java
new file mode 100644
index 0000000..118a8ee
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/profile/ECMCBadIdentityException.java
@@ -0,0 +1,54 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.profile;
+
+/**
+ * This represents a profile specific exception for handling
+ * CMC badIdentity condition.
+ * The framework raises this exception when a request fails identity
+ * checks
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ECMCBadIdentityException extends EProfileException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -89147145684990870L;
+
+    /**
+     * Creates an exception.
+     *
+     * @param msg localized message that will be
+     *            displayed to end user.
+     */
+    public ECMCBadIdentityException(String msg) {
+        super(msg);
+    }
+
+    public ECMCBadIdentityException(Throwable cause) {
+        super(cause.getMessage(), cause);
+    }
+
+    public ECMCBadIdentityException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/profile/ECMCBadMessageCheckException.java b/base/common/src/com/netscape/certsrv/profile/ECMCBadMessageCheckException.java
new file mode 100644
index 0000000..fb56eb4
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/profile/ECMCBadMessageCheckException.java
@@ -0,0 +1,54 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.profile;
+
+/**
+ * This represents a profile specific exception for handling
+ * CMC badMessageCheck condition.
+ * The framework raises this exception when a request fails various
+ * checks
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ECMCBadMessageCheckException extends EProfileException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1353005739159030604L;
+
+    /**
+     * Creates an exception.
+     *
+     * @param msg localized message that will be
+     *            displayed to end user.
+     */
+    public ECMCBadMessageCheckException(String msg) {
+        super(msg);
+    }
+
+    public ECMCBadMessageCheckException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+    public ECMCBadMessageCheckException(Throwable cause) {
+        super(cause.getMessage(), cause);
+    }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/profile/ECMCBadRequestException.java b/base/common/src/com/netscape/certsrv/profile/ECMCBadRequestException.java
new file mode 100644
index 0000000..dc4a107
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/profile/ECMCBadRequestException.java
@@ -0,0 +1,53 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.profile;
+
+/**
+ * This represents a profile specific exception for handling
+ * CMC badRequest condition.
+ * The framework raises this exception when a request fails
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ECMCBadRequestException extends EProfileException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -957171725482446695L;
+
+    /**
+     * Creates an exception.
+     *
+     * @param msg localized message that will be
+     *            displayed to end user.
+     */
+    public ECMCBadRequestException(String msg) {
+        super(msg);
+    }
+
+    public ECMCBadRequestException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+    public ECMCBadRequestException(Throwable cause) {
+        super(cause.getMessage(), cause);
+    }
+
+}
diff --git a/base/common/src/com/netscape/certsrv/profile/ECMCPopFailedException.java b/base/common/src/com/netscape/certsrv/profile/ECMCPopFailedException.java
new file mode 100644
index 0000000..cc87434
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/profile/ECMCPopFailedException.java
@@ -0,0 +1,53 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.profile;
+
+/**
+ * This represents a profile specific exception for handling
+ * CMC popFailed condition.
+ * The framework raises this exception when a request fails POP
+ * (Proof Of Possession) check
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ECMCPopFailedException extends EProfileException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -3098694565652563197L;
+
+    /**
+     * Creates an exception.
+     *
+     * @param msg localized message that will be
+     *            displayed to end user.
+     */
+    public ECMCPopFailedException(String msg) {
+        super(msg);
+    }
+
+    public ECMCPopFailedException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+    public ECMCPopFailedException(Throwable cause) {
+        super(cause.getMessage(), cause);
+    }
+}
diff --git a/base/common/src/com/netscape/certsrv/profile/ECMCPopRequiredException.java b/base/common/src/com/netscape/certsrv/profile/ECMCPopRequiredException.java
new file mode 100644
index 0000000..25a7bc0
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/profile/ECMCPopRequiredException.java
@@ -0,0 +1,59 @@
+// --- 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) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.profile;
+
+/**
+ * This represents a profile specific exception for handling
+ * CMC popRequired condition.
+ * The framework raises this exception when a request is missing POP
+ * (Proof Of Possession)
+ * <p>
+ * A CMC request with missing POP will not be processed immediately.
+ * Round trip is required to return with CMC direct POP (DecryptedPOP)
+ * for processing the request again.
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ECMCPopRequiredException extends EProfileException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 8328983412028345364L;
+
+    /**
+     * Creates a defer exception.
+     *
+     * @param msg localized message that will be
+     *            displayed to end user. This message
+     *            should indicate the reason why a request
+     *            is deferred.
+     */
+    public ECMCPopRequiredException(String msg) {
+        super(msg);
+    }
+
+    public ECMCPopRequiredException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+    public ECMCPopRequiredException(Throwable cause) {
+        super(cause.getMessage(), cause);
+    }
+}
diff --git a/base/common/src/com/netscape/certsrv/profile/ECMCUnsupportedExtException.java b/base/common/src/com/netscape/certsrv/profile/ECMCUnsupportedExtException.java
new file mode 100644
index 0000000..b33c58a
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/profile/ECMCUnsupportedExtException.java
@@ -0,0 +1,53 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2017 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.profile;
+
+/**
+ * This represents a profile specific exception for handling
+ * CMC unsupportedExt condition.
+ * The framework raises this exception when a request contains extensions
+ * that's not supported
+ * <p>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ECMCUnsupportedExtException extends EProfileException {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -2065658791983639446L;
+
+    /**
+     * Creates an exception.
+     *
+     * @param msg localized message that will be
+     *            displayed to end user.
+     */
+    public ECMCUnsupportedExtException(String msg) {
+        super(msg);
+    }
+
+    public ECMCUnsupportedExtException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+    public ECMCUnsupportedExtException(Throwable cause) {
+        super(cause.getMessage(), cause);
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
index 00e03a7..fd59aa1 100644
--- a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
+++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
@@ -65,7 +65,7 @@ import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.pkcs10.CertificationRequest;
 import org.mozilla.jss.pkcs10.CertificationRequestInfo;
 import org.mozilla.jss.pkix.cmc.CMCCertId;
-import org.mozilla.jss.pkix.cmc.CMCStatusInfo;
+import org.mozilla.jss.pkix.cmc.CMCStatusInfoV2;
 import org.mozilla.jss.pkix.cmc.DecryptedPOP;
 import org.mozilla.jss.pkix.cmc.EncryptedPOP;
 import org.mozilla.jss.pkix.cmc.GetCert;
@@ -1609,14 +1609,15 @@ public class CMCRequest {
      *
      * @param prevResponse file
      * @param privKey
-     * @return encryptedPop and reqIdString in Object[]
+     * @return encryptedPop and reqIdOS (requestID in Octet String in Object[]
      * @author cfu
      */
     private static Object[] processEncryptedPopResponse(
             String prevResponse) {
         // the values to be returned
         EncryptedPOP encryptedPop = null;
-        String reqIdString = null; // capture the requestId;
+        String reqIdString = null;
+        OCTET_STRING reqIdOS = null; // capture the requestId;
 
         String method = "processEncryptedPopResponse: ";
         System.out.println(method + " begins.");
@@ -1661,13 +1662,13 @@ public class CMCRequest {
                 TaggedAttribute taggedAttr = (TaggedAttribute) controlSequence.elementAt(i);
                 OBJECT_IDENTIFIER type = taggedAttr.getType();
 
-                if (type.equals(OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo)) {
-                    System.out.println(method + "Control #" + i + ": CMCStatusInfo");
+                if (type.equals(OBJECT_IDENTIFIER.id_cmc_statusInfoV2)) {
+                    System.out.println(method + "Control #" + i + ": CMCStatusInfoV2");
                     System.out.println(method + "   OID: " + type.toString());
                     SET sts = taggedAttr.getValues();
                     int numSts = sts.size();
                     for (int j = 0; j < numSts; j++) {
-                        CMCStatusInfo cst = (CMCStatusInfo) ASN1Util.decode(CMCStatusInfo.getTemplate(),
+                        CMCStatusInfoV2 cst = (CMCStatusInfoV2) ASN1Util.decode(CMCStatusInfoV2.getTemplate(),
                                 ASN1Util.encode(sts.elementAt(j)));
                         SEQUENCE seq = cst.getBodyList();
                         StringBuilder s = new StringBuilder("   BodyList: ");
@@ -1677,7 +1678,7 @@ public class CMCRequest {
                         }
                         System.out.println(method + s);
                         int st = cst.getStatus();
-                        if (st != CMCStatusInfo.SUCCESS && st != CMCStatusInfo.CONFIRM_REQUIRED) {
+                        if (st != CMCStatusInfoV2.SUCCESS && st != CMCStatusInfoV2.CONFIRM_REQUIRED) {
                             String stString = cst.getStatusString();
                             if (stString != null)
                                 System.out.println(method + "   Status String: " + stString);
@@ -1685,9 +1686,22 @@ public class CMCRequest {
                             OtherInfo.Type t = oi.getType();
                             if (t == OtherInfo.FAIL) {
                                 System.out.println(method + "   OtherInfo type: FAIL");
-                                System.out.println(method
-                                        + " not what we expected, because encryptedPOP.enable is true!!!! exit now");
-                                System.exit(1);
+                                INTEGER failInfo = oi.getFailInfo();
+                                if (failInfo == null) {
+                                    System.out.println(method + "failInfo null...skipping");
+                                    continue;
+                                }
+
+                                if (failInfo.intValue() == OtherInfo.POP_REQUIRED) {
+                                    System.out.println(method + "     failInfo=" +
+                                            OtherInfo.FAIL_INFO[failInfo.intValue()]);
+                                    System.out.println(method + "   what we expected, as decryptedPOP.enable is true;");
+                                } else {
+                                    System.out.println(method + "failInfo=" +
+                                            OtherInfo.FAIL_INFO[failInfo.intValue()]);
+                                    System.out.println(method + " not what we expected when encryptedPOP.enable is true;");
+                                    System.exit(1);
+                                }
                             } else if (t == OtherInfo.PEND) {
                                 System.out.println(method + "   OtherInfo type: PEND");
                                 PendInfo pi = oi.getPendInfo();
@@ -1711,9 +1725,8 @@ public class CMCRequest {
                                     System.out.println(method + "missing pendToken in response");
                                     System.exit(1);
                                 }
-                                System.out.println(method + " what we expected, as encryptedPOP.enable is true;");
                             }
-                        } else if (st == CMCStatusInfo.SUCCESS) {
+                        } else if (st == CMCStatusInfoV2.SUCCESS) {
                             System.out.println(method + "   Status: SUCCESS");
                             System.out.println(
                                     method + " not what we expected, because encryptedPOP.enable is true!!!! exit now");
@@ -1728,8 +1741,18 @@ public class CMCRequest {
 
                     encryptedPop = (EncryptedPOP) (ASN1Util.decode(EncryptedPOP.getTemplate(),
                             ASN1Util.encode(encryptedPOPvals.elementAt(0))));
-                    System.out.println(method + "encryptedPOP decoded successfully");
-
+                    System.out.println(method + "     encryptedPOP decoded successfully");
+
+                } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_responseInfo)) {
+                    System.out.println(method + "Control #" + i + ": CMC ResponseInfo");
+                    SET riVals = taggedAttr.getValues();
+                    reqIdOS = (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
+                           ASN1Util.encode(riVals.elementAt(0))));
+                    byte[] reqIdBA = reqIdOS.toByteArray();
+                    BigInteger reqIdBI = new BigInteger(reqIdBA);
+
+                    System.out.println(method + "   requestID: " + reqIdBI.toString());
+                            
                 } // we don't expect any other controls
             } //for
         } catch (Exception e) {
@@ -1738,13 +1761,13 @@ public class CMCRequest {
         }
 
         System.out.println(method + "ends");
-        return new Object[] { encryptedPop, reqIdString };
+        return new Object[] { encryptedPop, reqIdOS };
     }
 
     /**
      * constructDecryptedPopRequest constructs request PKIData for DecryptedPOP
      *
-     * @param encryptedPopInfo {EncryptedPOP, reqIdString}
+     * @param encryptedPopInfo {EncryptedPOP, reqIdOS}
      * @param privKey
      * @return request PKIData
      * @author cfu
@@ -1764,8 +1787,8 @@ public class CMCRequest {
         }
 
         EncryptedPOP encryptedPop = (EncryptedPOP) encryptedPopInfo[0];
-        String reqIdString = (String) encryptedPopInfo[1];
-        if ((encryptedPop == null) || (reqIdString == null)) {
+        OCTET_STRING reqIdOS = (OCTET_STRING) encryptedPopInfo[1];
+        if ((encryptedPop == null) || (reqIdOS == null)) {
             System.out.println(method + "encryptedPopInfo content encryptedPop and reqIdString cannot be null");
             System.exit(1);
         }
@@ -1851,7 +1874,8 @@ public class CMCRequest {
             int bpid = 1;
             // now construct DecryptedPOP
             System.out.println(method + "constructing DecryptedPOP...");
-            decryptedPop = new DecryptedPOP(new INTEGER(reqIdString), thePOPAlgID, new OCTET_STRING(popProofValue));
+
+            decryptedPop = new DecryptedPOP(new INTEGER(bpid++), thePOPAlgID, new OCTET_STRING(popProofValue));
             System.out.println(method + "DecryptedPOP constructed successfully");
             System.out.println(method + "adding decryptedPop control");
             TaggedAttribute decPop = new TaggedAttribute(new INTEGER(bpid++),
@@ -1865,6 +1889,13 @@ public class CMCRequest {
             controlSeq.addElement(decPop);
             System.out.println(method + "decryptedPop control added");
 
+            TaggedAttribute reqIdTA = 
+                        new TaggedAttribute(new INTEGER(bpid++),
+                        OBJECT_IDENTIFIER.id_cmc_regInfo,
+                        reqIdOS);
+            controlSeq.addElement(reqIdTA);
+            System.out.println(method + "regInfo control added");
+
             SEQUENCE otherMsgSeq = new SEQUENCE();
 
             pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq);
diff --git a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java
index 4c74934..5224de8 100644
--- a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java
+++ b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java
@@ -19,6 +19,7 @@ package com.netscape.cmstools;
 
 import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
+import java.math.BigInteger;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Locale;
@@ -33,7 +34,7 @@ import org.mozilla.jss.asn1.OCTET_STRING;
 import org.mozilla.jss.asn1.SEQUENCE;
 import org.mozilla.jss.asn1.SET;
 import org.mozilla.jss.pkix.cert.Certificate;
-import org.mozilla.jss.pkix.cmc.CMCStatusInfo;
+import org.mozilla.jss.pkix.cmc.CMCStatusInfoV2;
 import org.mozilla.jss.pkix.cmc.EncryptedPOP;
 import org.mozilla.jss.pkix.cmc.OtherInfo;
 import org.mozilla.jss.pkix.cmc.PendInfo;
@@ -113,13 +114,13 @@ public class CMCResponse {
                 TaggedAttribute taggedAttr = (TaggedAttribute) controlSequence.elementAt(i);
                 OBJECT_IDENTIFIER type = taggedAttr.getType();
 
-                if (type.equals(OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo)) {
-                    System.out.println("Control #" + i + ": CMCStatusInfo");
+                if (type.equals(OBJECT_IDENTIFIER.id_cmc_statusInfoV2)) {
+                    System.out.println("Control #" + i + ": CMCStatusInfoV2");
                     System.out.println("   OID: " + type.toString());
                     SET sts = taggedAttr.getValues();
                     int numSts = sts.size();
                     for (int j = 0; j < numSts; j++) {
-                        CMCStatusInfo cst = (CMCStatusInfo) ASN1Util.decode(CMCStatusInfo.getTemplate(),
+                        CMCStatusInfoV2 cst = (CMCStatusInfoV2) ASN1Util.decode(CMCStatusInfoV2.getTemplate(),
                                 ASN1Util.encode(sts.elementAt(j)));
                         SEQUENCE seq = cst.getBodyList();
 
@@ -130,15 +131,23 @@ public class CMCResponse {
                         }
                         System.out.println(s);
                         int st = cst.getStatus();
-                        if (st != CMCStatusInfo.SUCCESS && st != CMCStatusInfo.CONFIRM_REQUIRED) {
+                        if (st != CMCStatusInfoV2.SUCCESS && st != CMCStatusInfoV2.CONFIRM_REQUIRED) {
                             String stString = cst.getStatusString();
                             if (stString != null)
                                 System.out.println("   Status String: " + stString);
                             OtherInfo oi = cst.getOtherInfo();
                             OtherInfo.Type t = oi.getType();
-                            if (t == OtherInfo.FAIL)
+                            if (t == OtherInfo.FAIL) {
                                 System.out.println("   OtherInfo type: FAIL");
-                            else if (t == OtherInfo.PEND) {
+                                INTEGER failInfo = oi.getFailInfo();
+                                if (failInfo == null) {
+                                    System.out.println("failInfo null...skipping");
+                                    continue;
+                                }
+
+                                System.out.println("     failInfo=" +
+                                        OtherInfo.FAIL_INFO[failInfo.intValue()]);
+                            } else if (t == OtherInfo.PEND) {
                                 System.out.println("   OtherInfo type: PEND");
                                 PendInfo pi = oi.getPendInfo();
                                 if (pi == null) {
@@ -163,7 +172,7 @@ public class CMCResponse {
                                 }
 
                             }
-                        } else if (st == CMCStatusInfo.SUCCESS) {
+                        } else if (st == CMCStatusInfoV2.SUCCESS) {
                             System.out.println("   Status: SUCCESS");
                         }
                     }
@@ -224,8 +233,17 @@ public class CMCResponse {
                     EncryptedPOP encryptedPOP =
                         (EncryptedPOP) (ASN1Util.decode(EncryptedPOP.getTemplate(),
                             ASN1Util.encode(encryptedPOPvals.elementAt(0))));
-                    System.out.println("after encryptedPOP encode");
+                    System.out.println("     encryptedPOP decoded");
+
+                } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_responseInfo)) {
+                    System.out.println("Control #" + i + ": CMC ResponseInfo");
+                    SET riVals = taggedAttr.getValues();
+                    OCTET_STRING reqIdOS = (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
+                           ASN1Util.encode(riVals.elementAt(0))));
+                    byte[] reqIdBA = reqIdOS.toByteArray();
+                    BigInteger reqIdBI = new BigInteger(reqIdBA);
 
+                    System.out.println("   requestID: " + reqIdBI.toString());
                 }
             }
         } catch (Exception e) {
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 74da8e7..8f3e986 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
@@ -84,6 +84,12 @@ import com.netscape.certsrv.base.SessionContext;
 import com.netscape.certsrv.ca.ICertificateAuthority;
 import com.netscape.certsrv.logging.AuditEvent;
 import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.profile.ECMCBadIdentityException;
+import com.netscape.certsrv.profile.ECMCBadMessageCheckException;
+import com.netscape.certsrv.profile.ECMCBadRequestException;
+import com.netscape.certsrv.profile.ECMCPopFailedException;
+import com.netscape.certsrv.profile.ECMCPopRequiredException;
+import com.netscape.certsrv.profile.ECMCUnsupportedExtException;
 import com.netscape.certsrv.profile.EDeferException;
 import com.netscape.certsrv.profile.EProfileException;
 import com.netscape.certsrv.profile.ERejectException;
@@ -550,7 +556,7 @@ public abstract class EnrollProfile extends BasicProfile
                 throw new EProfileException(msg);
             }
 
-            throw new EDeferException("EnrollProfile: submit: encryptedPOP defer request");
+            throw new ECMCPopRequiredException(" Return  with DecryptedPOP to complete");
 
         } else {
             // this profile executes request that is authenticated
@@ -726,6 +732,8 @@ public abstract class EnrollProfile extends BasicProfile
                     TaggedAttribute[] attributes = new TaggedAttribute[numcontrols];
                     boolean id_cmc_decryptedPOP = false;
                     SET decPopVals = null;
+                    boolean id_cmc_regInfo = false;
+                    SET reqIdVals = null;
 
                     boolean id_cmc_identification = false;
                     SET ident = null;
@@ -756,6 +764,10 @@ public abstract class EnrollProfile extends BasicProfile
                             CMS.debug(method + " id_cmc_decryptedPOP found");
                             id_cmc_decryptedPOP = true;
                             decPopVals = attributes[i].getValues();
+                        } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_regInfo)) {
+                            CMS.debug(method + "id_cmc_regInfo found");
+                            id_cmc_regInfo = true;
+                            reqIdVals = attributes[i].getValues();
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) {
                             CMS.debug(method + " id_cmc_identification found");
                             id_cmc_identification = true;
@@ -796,11 +808,10 @@ public abstract class EnrollProfile extends BasicProfile
 
                             msg = " id_cmc_identification attribute value not found in";
                             CMS.debug(method + msg);
-/*
-                            throw new EProfileException(
-                                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") +
+
+                            throw new ECMCBadRequestException(
+                                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") + ":" +
                                             msg);
-*/
                         } else {
                             ident_s = (UTF8String) (ASN1Util.decode(UTF8String.getTemplate(),
                                     ASN1Util.encode(ident.elementAt(0))));
@@ -812,11 +823,11 @@ public abstract class EnrollProfile extends BasicProfile
                             context.put("identification", bpids);
 
                             CMS.debug(method + msg);
-/*
-                            throw new EProfileException(
-                                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") +
+
+                            throw new ECMCBadRequestException(
+                                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") + ":" +
                                             msg);
-*/
+
                         }
                     }
 
@@ -850,8 +861,8 @@ public abstract class EnrollProfile extends BasicProfile
                                     method + msg);
                             audit(auditMessage);
 
-                            throw new EProfileException(
-                                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") +
+                            throw new ECMCBadIdentityException(
+                                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") + ":" +
                                             msg);
                         }
 
@@ -863,7 +874,7 @@ public abstract class EnrollProfile extends BasicProfile
 
                             msg = " after verifyIdentityProofV2";
                             CMS.debug(method + msg);
-                            throw new EProfileException(CMS.getUserMessage(locale,
+                            throw new ECMCBadIdentityException(CMS.getUserMessage(locale,
                                     "CMS_POI_VERIFICATION_ERROR") + msg);
                         } else {
                             CMS.debug(method + "passed verifyIdentityProofV2; Proof of Identity successful;");
@@ -878,7 +889,7 @@ public abstract class EnrollProfile extends BasicProfile
 
                             msg = " after verifyIdentityProof";
                             CMS.debug(method + msg);
-                            throw new EProfileException(CMS.getUserMessage(locale,
+                            throw new ECMCBadIdentityException(CMS.getUserMessage(locale,
                                     "CMS_POI_VERIFICATION_ERROR") + msg);
                         } else {
                             CMS.debug(method + "passed verifyIdentityProof; Proof of Identity successful;");
@@ -894,20 +905,53 @@ public abstract class EnrollProfile extends BasicProfile
                                 ILogger.FAILURE,
                                 method + msg);
                         audit(auditMessage);
-                        throw new EProfileException(CMS.getUserMessage(locale,
-                                "CMS_POI_VERIFICATION_ERROR") + ":" + method + msg);
+                        throw new ECMCBadRequestException(CMS.getUserMessage(locale,
+                                "CMS_POI_VERIFICATION_ERROR") + ":" + msg);
                     }
 
                     if (id_cmc_decryptedPOP) {
                         if (decPopVals != null) {
+                            if (!id_cmc_regInfo) {
+                                msg = "id_cmc_decryptedPOP must be accompanied by id_cmc_regInfo for request id per server/client agreement";
+                                CMS.debug(method + msg);
+                                auditMessage = CMS.getLogMessage(
+                                        AuditEvent.PROOF_OF_POSSESSION,
+                                        auditSubjectID,
+                                        ILogger.FAILURE,
+                                        method + msg);
+                                audit(auditMessage);
+
+                                SEQUENCE bpids = getRequestBpids(reqSeq);
+                                context.put("decryptedPOP", bpids);
+                                throw new ECMCPopFailedException(CMS.getUserMessage(locale,
+                                        "CMS_POP_VERIFICATION_ERROR") + ":" + msg);
+                            }
+
+                            OCTET_STRING reqIdOS =
+                                    (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
+                                    ASN1Util.encode(reqIdVals.elementAt(0))));
 
                             DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(),
                                     ASN1Util.encode(decPopVals.elementAt(0))));
                             CMS.debug(method + "DecryptedPOP encoded");
 
-                            Integer reqId = verifyDecryptedPOP(locale, decPop);
+                            BigInteger reqId = verifyDecryptedPOP(locale, decPop, reqIdOS);
                             if (reqId != null) {
                                 context.put("cmcDecryptedPopReqId", reqId);
+                            } else {
+                                msg = "DecryptedPOP failed to verify";
+                                CMS.debug(method + msg);
+                                auditMessage = CMS.getLogMessage(
+                                        AuditEvent.PROOF_OF_POSSESSION,
+                                        auditSubjectID,
+                                        ILogger.FAILURE,
+                                        method + msg);
+                                audit(auditMessage);
+
+                                SEQUENCE bpids = getRequestBpids(reqSeq);
+                                context.put("decryptedPOP", bpids);
+                                throw new ECMCPopFailedException(CMS.getUserMessage(locale,
+                                        "CMS_POP_VERIFICATION_ERROR") + ":" + msg);
                             }
                         } else { //decPopVals == null
                             msg = "id_cmc_decryptedPOP contains invalid DecryptedPOP";
@@ -915,12 +959,14 @@ public abstract class EnrollProfile extends BasicProfile
                             auditMessage = CMS.getLogMessage(
                                     AuditEvent.PROOF_OF_POSSESSION,
                                     auditSubjectID,
-                                    ILogger.SUCCESS,
+                                    ILogger.FAILURE,
                                     method + msg);
                             audit(auditMessage);
 
                             SEQUENCE bpids = getRequestBpids(reqSeq);
                             context.put("decryptedPOP", bpids);
+                            throw new ECMCPopFailedException(CMS.getUserMessage(locale,
+                                    "CMS_POP_VERIFICATION_ERROR") + ":" + msg);
                         }
 
                         // decryptedPOP is expected to return null;
@@ -967,9 +1013,9 @@ public abstract class EnrollProfile extends BasicProfile
                 }
             } catch (Exception e) {
                 // unlikely to get here
-                msg = method + " Failed to retrieve cmc.popLinkWitnessRequired";
-                CMS.debug(msg);
-                throw new EProfileException(method + msg);
+                msg = " Failed to retrieve cmc.popLinkWitnessRequired";
+                CMS.debug(method + msg);
+                throw new EProfileException( msg);
             }
 
             int nummsgs = reqSeq.size();
@@ -988,7 +1034,7 @@ public abstract class EnrollProfile extends BasicProfile
                             !context.containsKey("POPLinkWitness")) {
                         CMS.debug(method + "popLinkWitness(V2) required");
                         if (randomSeed == null || ident_s == null) {
-                            msg = "no randomSeed or identification found needed for popLinkWitness(V2)";
+                            msg = "missing needed randomSeed or identification for popLinkWitness(V2)";
                             CMS.debug(method + msg);
                             auditMessage = CMS.getLogMessage(
                                     AuditEvent.CMC_ID_POP_LINK_WITNESS,
@@ -998,7 +1044,8 @@ public abstract class EnrollProfile extends BasicProfile
                             audit(auditMessage);
 
                             context.put("POPLinkWitnessV2", bpids);
-                            return null;
+                            throw new ECMCBadRequestException(CMS.getUserMessage(locale,
+                                    "CMS_POP_LINK_WITNESS_VERIFICATION_ERROR") + ":" + msg);
                         }
 
                         // verifyPOPLinkWitness() will determine if this is
@@ -1021,8 +1068,8 @@ public abstract class EnrollProfile extends BasicProfile
                                     ILogger.FAILURE,
                                     method + msg);
                             audit(auditMessage);
-                            throw new EProfileException(CMS.getUserMessage(locale,
-                                    "CMS_POP_LINK_WITNESS_VERIFICATION_ERROR") + msg);
+                            throw new ECMCBadRequestException(CMS.getUserMessage(locale,
+                                    "CMS_POP_LINK_WITNESS_VERIFICATION_ERROR") + ":" + msg);
                         } else {
                             msg = ": ident_s=" + ident_s;
                             auditMessage = CMS.getLogMessage(
@@ -1041,6 +1088,14 @@ public abstract class EnrollProfile extends BasicProfile
 
             CMS.debug(method + "ends");
             return msgs;
+        } catch (ECMCBadMessageCheckException e) {
+            throw new ECMCBadMessageCheckException(e);
+        } catch (ECMCBadIdentityException e) {
+            throw new ECMCBadIdentityException(e);
+        } catch (ECMCPopFailedException e) {
+            throw new ECMCPopFailedException(e);
+        } catch (ECMCBadRequestException e) {
+            throw new ECMCBadRequestException(e);
         } catch (EProfileException e) {
             throw new EProfileException(e);
         } catch (Exception e) {
@@ -1056,34 +1111,28 @@ public abstract class EnrollProfile extends BasicProfile
      *
      * @author cfu
      */
-    private Integer verifyDecryptedPOP(Locale locale, DecryptedPOP decPop)
-            throws EProfileException {
+    private BigInteger verifyDecryptedPOP(Locale locale,
+            DecryptedPOP decPop,
+            OCTET_STRING reqIdOS)
+            throws EProfileException, ECMCPopFailedException {
         String method = "EnrollProfile: verifyDecryptedPOP: ";
         CMS.debug(method + "begins");
         String msg = "";
 
-        if (decPop == null) {
+        if (decPop == null || reqIdOS == null) {
             CMS.debug(method + "method parameters cannot be null");
             return null;
         }
 
-        // iBody contains the request id
-        INTEGER iBody = decPop.getBodyPartID();
-        if (iBody == null) {
-            msg = method + "iBody null after decPop.getBodyPartID";
-            CMS.debug(msg);
-            return null;
-        }
-        CMS.debug(method + "request id from decryptedPOP =" +
-                iBody.toString());
-        Integer reqId = new Integer(iBody.toString());
+        byte[] reqIdBA = reqIdOS.toByteArray();
+        BigInteger reqIdBI = new BigInteger(reqIdBA);
 
         OCTET_STRING witness_os = decPop.getWitness();
 
         IRequestQueue reqQueue = getRequestQueue();
         IRequest req = null;
         try {
-            req = reqQueue.findRequest(new RequestId(reqId));
+            req = reqQueue.findRequest(new RequestId(reqIdBI));
         } catch (Exception e) {
             msg = method + "after findRequest: " + e;
             CMS.debug(msg);
@@ -1095,7 +1144,7 @@ public abstract class EnrollProfile extends BasicProfile
         if (pop_encryptedData == null) {
             msg = method +
                     "pop_encryptedData not found in request:" +
-                    reqId.toString();
+                    reqIdBI.toString();
             CMS.debug(msg);
             return null;
         }
@@ -1104,7 +1153,7 @@ public abstract class EnrollProfile extends BasicProfile
         if (pop_sysPubEncryptedSession == null) {
             msg = method +
                     "pop_sysPubEncryptedSession not found in request:" +
-                    reqId.toString();
+                    reqIdBI.toString();
             CMS.debug(msg);
             return null;
         }
@@ -1113,7 +1162,7 @@ public abstract class EnrollProfile extends BasicProfile
         if (cmc_msg == null) {
             msg = method +
                     "cmc_msg not found in request:" +
-                    reqId.toString();
+                    reqIdBI.toString();
             CMS.debug(msg);
             return null;
         }
@@ -1185,8 +1234,8 @@ public abstract class EnrollProfile extends BasicProfile
                 return null;
             }
         } catch (Exception e) {
-            msg = method + e;
-            CMS.debug(msg);
+            msg = e.toString();
+            CMS.debug(method + msg);
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") +
                             e);
@@ -1198,7 +1247,7 @@ public abstract class EnrollProfile extends BasicProfile
         CMS.debug(method + "cmc_POPchallengeRequired set back to false");
         CMS.debug(method + "ends");
 
-        return reqId;
+        return reqIdBI;
     }
 
     /**
@@ -1645,7 +1694,9 @@ public abstract class EnrollProfile extends BasicProfile
                         "method=" + method);
                 audit(auditMessage);
             } else {
-                throw new EBaseException("failed to verify");
+                msg = "IdentityProofV2 failed to verify";
+                CMS.debug(method + msg);
+                throw new EBaseException(msg);
             }
             return verified;
         } catch (Exception e) {
@@ -1677,28 +1728,28 @@ public abstract class EnrollProfile extends BasicProfile
             return false;
         }
 
-            String token = tokenClass.getSharedToken(mCMCData);
-            OCTET_STRING ostr = null;
-            try {
-                ostr = (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
-                        ASN1Util.encode(vals.elementAt(0))));
-            } catch (InvalidBERException e) {
-                CMS.debug(method + "Failed to decode the byte value.");
-                return false;
-            }
-            byte[] b = ostr.toByteArray();
-            byte[] text = ASN1Util.encode(reqSeq);
+        String token = tokenClass.getSharedToken(mCMCData);
+        OCTET_STRING ostr = null;
+        try {
+            ostr = (OCTET_STRING) (ASN1Util.decode(OCTET_STRING.getTemplate(),
+                    ASN1Util.encode(vals.elementAt(0))));
+        } catch (InvalidBERException e) {
+            CMS.debug(method + "Failed to decode the byte value.");
+            return false;
+        }
+        byte[] b = ostr.toByteArray();
+        byte[] text = ASN1Util.encode(reqSeq);
 
-            verified = verifyDigest(token.getBytes(), text, b);
-            if (verified) {// update auditSubjectID
-                //placeholder. Should probably just disable this v1 method
-            }
-            return verified;
+        verified = verifyDigest(token.getBytes(), text, b);
+        if (verified) {// update auditSubjectID
+            //placeholder. Should probably just disable this v1 method
+        }
+        return verified;
     }
 
     public void fillTaggedRequest(Locale locale, TaggedRequest tagreq, X509CertInfo info,
             IRequest req)
-            throws EProfileException {
+            throws EProfileException, ECMCPopFailedException, ECMCBadRequestException {
         String auditMessage = null;
         String auditSubjectID = auditSubjectID();
 
@@ -1832,7 +1883,7 @@ public abstract class EnrollProfile extends BasicProfile
             fillCertReqMsg(locale, crm, info, req);
         } else {
             CMS.debug(method + " unsupported type (not CRMF or PKCS10)");
-            throw new EProfileException(
+            throw new ECMCBadRequestException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"));
         }
     }
@@ -1951,7 +2002,7 @@ public abstract class EnrollProfile extends BasicProfile
 
     public void fillCertReqMsg(Locale locale, CertReqMsg certReqMsg, X509CertInfo info,
             IRequest req)
-            throws EProfileException {
+            throws EProfileException, ECMCUnsupportedExtException {
         String method = "EnrollProfile: fillCertReqMsg: ";
         try {
             CMS.debug(method + "Start parseCertReqMsg ");
@@ -2111,7 +2162,7 @@ public abstract class EnrollProfile extends BasicProfile
         } catch (IOException e) {
             CMS.debug("EnrollProfile: Unable to fill certificate request message: " + e);
             CMS.debug(e);
-            throw new EProfileException(
+            throw new ECMCUnsupportedExtException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e);
         } catch (InvalidKeyException e) {
             CMS.debug("EnrollProfile: Unable to fill certificate request message: " + e);
@@ -2175,7 +2226,7 @@ public abstract class EnrollProfile extends BasicProfile
     }
 
     public void fillPKCS10(Locale locale, PKCS10 pkcs10, X509CertInfo info, IRequest req)
-            throws EProfileException {
+            throws EProfileException, ECMCUnsupportedExtException {
         String method = "EnrollProfile: fillPKCS10: ";
         CMS.debug(method + "begins");
         X509Key key = pkcs10.getSubjectPublicKeyInfo();
@@ -2234,7 +2285,7 @@ public abstract class EnrollProfile extends BasicProfile
             CMS.debug(method + "Finish parsePKCS10 - " + pkcs10.getSubjectName());
         } catch (IOException e) {
             CMS.debug(method + "Unable to fill PKCS #10: " + e);
-            throw new EProfileException(
+            throw new ECMCUnsupportedExtException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e);
         } catch (CertificateException e) {
             CMS.debug(method + "Unable to fill PKCS #10: " + e);
@@ -2582,7 +2633,7 @@ public abstract class EnrollProfile extends BasicProfile
      * verifyPOP - CRMF POP verification for signing keys
      */
     public void verifyPOP(Locale locale, CertReqMsg certReqMsg)
-            throws EProfileException {
+            throws EProfileException, ECMCPopFailedException {
         String method = "EnrollProfile: verifyPOP: ";
         CMS.debug(method + "for signing keys begins.");
 
@@ -2628,11 +2679,11 @@ public abstract class EnrollProfile extends BasicProfile
     }
 
     private void popFailed(Locale locale, String auditSubjectID, String msg)
-            throws EProfileException {
+            throws EProfileException, ECMCPopFailedException {
         popFailed(locale, auditSubjectID, msg, null);
     }
     private void popFailed(Locale locale, String auditSubjectID, String msg, Exception e)
-            throws EProfileException {
+            throws EProfileException, ECMCPopFailedException {
 
             if (e != null)
                 msg = msg + e.toString();
@@ -2645,10 +2696,10 @@ public abstract class EnrollProfile extends BasicProfile
             audit(auditMessage);
 
             if (e != null) {
-                throw new EProfileException(CMS.getUserMessage(locale,
+                throw new ECMCPopFailedException(CMS.getUserMessage(locale,
                         "CMS_POP_VERIFICATION_ERROR"), e);
             } else {
-                throw new EProfileException(CMS.getUserMessage(locale,
+                throw new ECMCPopFailedException(CMS.getUserMessage(locale,
                         "CMS_POP_VERIFICATION_ERROR"));
             }
     }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java b/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java
index 067dce7..1e509d3 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/common/CMCOutputTemplate.java
@@ -49,7 +49,7 @@ import org.mozilla.jss.crypto.SignatureAlgorithm;
 import org.mozilla.jss.pkcs11.PK11PubKey;
 import org.mozilla.jss.pkix.cert.Certificate;
 import org.mozilla.jss.pkix.cmc.CMCCertId;
-import org.mozilla.jss.pkix.cmc.CMCStatusInfo;
+import org.mozilla.jss.pkix.cmc.CMCStatusInfoV2;
 import org.mozilla.jss.pkix.cmc.EncryptedPOP;
 import org.mozilla.jss.pkix.cmc.GetCert;
 import org.mozilla.jss.pkix.cmc.OtherInfo;
@@ -117,13 +117,13 @@ public class CMCOutputTemplate {
 
         int bpid = 1;
         OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                new INTEGER(code), null);
-        CMCStatusInfo cmcStatusInfo = new CMCStatusInfo(
-                new INTEGER(CMCStatusInfo.FAILED),
+                new INTEGER(code), null, null);
+        CMCStatusInfoV2 cmcStatusInfoV2 = new CMCStatusInfoV2(
+                new INTEGER(CMCStatusInfoV2.FAILED),
                 bpids, s, otherInfo);
         TaggedAttribute tagattr = new TaggedAttribute(
                 new INTEGER(bpid++),
-                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
         controlSeq.addElement(tagattr);
 
         try {
@@ -166,6 +166,7 @@ public class CMCOutputTemplate {
         // in rfc 2797: body list value is 1
         int bpid = 1;
         SEQUENCE pending_bpids = null;
+        SEQUENCE popRequired_bpids = null;
         SEQUENCE success_bpids = null;
         SEQUENCE failed_bpids = null;
         if (cert_request_type.equals("crmf") ||
@@ -175,23 +176,24 @@ public class CMCOutputTemplate {
             if (error_codes[0] == 2) {
                 PendInfo pendInfo = new PendInfo(reqId, new Date());
                 otherInfo = new OtherInfo(OtherInfo.PEND, null,
-                        pendInfo);
+                        pendInfo, null);
             } else {
                 otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_REQUEST), null);
+                        new INTEGER(OtherInfo.BAD_REQUEST), null, null);
             }
 
             SEQUENCE bpids = new SEQUENCE();
             bpids.addElement(new INTEGER(1));
-            CMCStatusInfo cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.PENDING,
+            CMCStatusInfoV2 cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.PENDING,
                     bpids, (String) null, otherInfo);
             TaggedAttribute tagattr = new TaggedAttribute(
                     new INTEGER(bpid++),
-                    OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                    OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
             controlSeq.addElement(tagattr);
         } else if (cert_request_type.equals("cmc")) {
             CMS.debug(method + " processing cmc");
             pending_bpids = new SEQUENCE();
+            popRequired_bpids = new SEQUENCE();
             success_bpids = new SEQUENCE();
             failed_bpids = new SEQUENCE();
             EncryptedPOP encPop = null;
@@ -205,11 +207,15 @@ public class CMCOutputTemplate {
                     } else if (error_codes[i] == 2) {
                         pending_bpids.addElement(new INTEGER(
                                 reqs[i].getExtDataInBigInteger("bodyPartId")));
+                    } else if (error_codes[i] == 4) {
+                        popRequired_bpids.addElement(new INTEGER(
+                                reqs[i].getExtDataInBigInteger("bodyPartId")));
                         try {
                             encPop = constructEncryptedPop(reqs[i]);
                         } catch (Exception e) {
                             CMS.debug(method + e);
-                            return;
+                            failed_bpids.addElement(new INTEGER(
+                                    reqs[i].getExtDataInBigInteger("bodyPartId")));
                         }
                     } else {
                         failed_bpids.addElement(new INTEGER(
@@ -221,41 +227,41 @@ public class CMCOutputTemplate {
             }
 
             TaggedAttribute tagattr = null;
-            CMCStatusInfo cmcStatusInfo = null;
+            CMCStatusInfoV2 cmcStatusInfoV2 = null;
 
             SEQUENCE decryptedPOPBpids = (SEQUENCE) context.get("decryptedPOP");
             if (decryptedPOPBpids != null && decryptedPOPBpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.POP_FAILED), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.POP_FAILED), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         decryptedPOPBpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
             SEQUENCE identificationBpids = (SEQUENCE) context.get("identification");
             if (identificationBpids != null && identificationBpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_IDENTITY), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.BAD_IDENTITY), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         identificationBpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
             SEQUENCE identityV2Bpids = (SEQUENCE) context.get("identityProofV2");
             if (identityV2Bpids != null && identityV2Bpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_IDENTITY), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.BAD_IDENTITY), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         identityV2Bpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
@@ -263,41 +269,41 @@ public class CMCOutputTemplate {
             SEQUENCE identityBpids = (SEQUENCE) context.get("identityProof");
             if (identityBpids != null && identityBpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_IDENTITY), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.BAD_IDENTITY), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         identityBpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
             SEQUENCE POPLinkWitnessV2Bpids = (SEQUENCE) context.get("POPLinkWitnessV2");
             if (POPLinkWitnessV2Bpids != null && POPLinkWitnessV2Bpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_REQUEST), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.BAD_REQUEST), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         POPLinkWitnessV2Bpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
             SEQUENCE POPLinkWitnessBpids = (SEQUENCE) context.get("POPLinkWitness");
             if (POPLinkWitnessBpids != null && POPLinkWitnessBpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_REQUEST), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.BAD_REQUEST), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         POPLinkWitnessBpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
-            if (pending_bpids.size() > 0) {
-                // handle encryptedPOP control first
+            if (popRequired_bpids.size() > 0) {
+                // handle encryptedPOP control
 
                 if (encPop != null) {
                     CMS.debug(method + "adding encPop");
@@ -309,17 +315,35 @@ public class CMCOutputTemplate {
                     CMS.debug(method + "encPop added");
                 }
 
+                OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
+                       new INTEGER(OtherInfo.POP_REQUIRED), null, null);
+                cmcStatusInfoV2 =
+                        new CMCStatusInfoV2(CMCStatusInfoV2.POP_REQUIRED,
+                        popRequired_bpids, (String) null, otherInfo);
+                tagattr = new TaggedAttribute(
+                        new INTEGER(bpid++),
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
+                controlSeq.addElement(tagattr);
+
+                // add request id
+                byte[] reqId = reqs[0].getRequestId().toBigInteger().toByteArray();
+                TaggedAttribute reqIdTA =
+                        new TaggedAttribute(new INTEGER(bpid++),
+                        OBJECT_IDENTIFIER.id_cmc_responseInfo,
+                        new OCTET_STRING(reqId));
+                controlSeq.addElement(reqIdTA);
+            }
+
+            if (pending_bpids.size() > 0) {
                 String reqId = reqs[0].getRequestId().toString();
-                OtherInfo otherInfo = null;
                 PendInfo pendInfo = new PendInfo(reqId, new Date());
-                otherInfo = new OtherInfo(OtherInfo.PEND, null,
-                        pendInfo);
-                // cfu: inject POP_REQUIRED when working on V2 status
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.PENDING,
+                OtherInfo otherInfo = new OtherInfo(OtherInfo.PEND, null,
+                        pendInfo, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.PENDING,
                         pending_bpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
@@ -333,27 +357,27 @@ public class CMCOutputTemplate {
                 }
                 if (confirmRequired) {
                     CMS.debug(method + " confirmRequired in the request");
-                    cmcStatusInfo =
-                            new CMCStatusInfo(CMCStatusInfo.CONFIRM_REQUIRED,
+                    cmcStatusInfoV2 =
+                            new CMCStatusInfoV2(CMCStatusInfoV2.CONFIRM_REQUIRED,
                                     success_bpids, (String) null, null);
                 } else {
-                    cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.SUCCESS,
+                    cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.SUCCESS,
                             success_bpids, (String) null, null);
                 }
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
 
             if (failed_bpids.size() > 0) {
                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                        new INTEGER(OtherInfo.BAD_REQUEST), null);
-                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                        new INTEGER(OtherInfo.BAD_REQUEST), null, null);
+                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                         failed_bpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                 controlSeq.addElement(tagattr);
             }
         }
@@ -373,15 +397,15 @@ public class CMCOutputTemplate {
                     } catch (EBaseException ee) {
                         CMS.debug(method + ee.toString());
                         OtherInfo otherInfo1 = new OtherInfo(OtherInfo.FAIL,
-                                new INTEGER(OtherInfo.BAD_CERT_ID), null);
+                                new INTEGER(OtherInfo.BAD_CERT_ID), null, null);
                         SEQUENCE bpids1 = new SEQUENCE();
                         bpids1.addElement(attr.getBodyPartID());
-                        CMCStatusInfo cmcStatusInfo1 = new CMCStatusInfo(
-                                new INTEGER(CMCStatusInfo.FAILED),
+                        CMCStatusInfoV2 cmcStatusInfoV2 = new CMCStatusInfoV2(
+                                new INTEGER(CMCStatusInfoV2.FAILED),
                                 bpids1, null, otherInfo1);
                         TaggedAttribute tagattr1 = new TaggedAttribute(
                                 new INTEGER(bpid++),
-                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo1);
+                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                         controlSeq.addElement(tagattr1);
                     }
                 }
@@ -537,7 +561,7 @@ public class CMCOutputTemplate {
             }
 
         } else {
-            msg = "popChallengeRequired required, but one more more of the pop_ data not found in request";
+            msg = "popChallengeRequired, but one or more of the pop_ data not found in request";
             CMS.debug(method + msg);
             throw new EBaseException(method + msg);
         }
@@ -734,23 +758,23 @@ public class CMCOutputTemplate {
                             CMS.debug("CMCOutputTemplate: Certificate in the confirm acceptance control was not found");
                         }
                     }
-                    CMCStatusInfo cmcStatusInfo = null;
+                    CMCStatusInfoV2 cmcStatusInfoV2 = null;
                     if (confirmAccepted) {
                         CMS.debug("CMCOutputTemplate: Confirm Acceptance received. The certificate exists in the certificate repository.");
-                        cmcStatusInfo =
-                                new CMCStatusInfo(CMCStatusInfo.SUCCESS, seq,
+                        cmcStatusInfoV2 =
+                                new CMCStatusInfoV2(CMCStatusInfoV2.SUCCESS, seq,
                                         (String) null, null);
                     } else {
                         CMS.debug("CMCOutputTemplate: Confirm Acceptance received. The certificate does not exist in the certificate repository.");
                         OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
-                                new INTEGER(OtherInfo.BAD_CERT_ID), null);
-                        cmcStatusInfo =
-                                new CMCStatusInfo(CMCStatusInfo.FAILED, seq,
+                                new INTEGER(OtherInfo.BAD_CERT_ID), null, null);
+                        cmcStatusInfoV2 =
+                                new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, seq,
                                         (String) null, otherInfo);
                     }
                     TaggedAttribute statustagattr = new TaggedAttribute(
                             new INTEGER(bpid++),
-                            OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                            OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                     controlSeq.addElement(statustagattr);
                 } catch (Exception e) {
                     CMS.debug("CMCOutputTemplate exception: " + e.toString());
@@ -825,28 +849,28 @@ public class CMCOutputTemplate {
                 }
 
                 if (pending_bpids.size() > 0) {
-                    CMCStatusInfo cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.PENDING,
+                    CMCStatusInfoV2 cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.PENDING,
                             pending_bpids, (String) null, null);
                     TaggedAttribute tagattr = new TaggedAttribute(
                             new INTEGER(bpid++),
-                            OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                            OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                     controlSeq.addElement(tagattr);
                 }
                 if (success_bpids.size() > 0) {
-                    CMCStatusInfo cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.SUCCESS,
+                    CMCStatusInfoV2 cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.SUCCESS,
                             pending_bpids, (String) null, null);
                     TaggedAttribute tagattr = new TaggedAttribute(
                             new INTEGER(bpid++),
-                            OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                            OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                     controlSeq.addElement(tagattr);
                 }
 
                 if (failed_bpids.size() > 0) {
-                    CMCStatusInfo cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED,
+                    CMCStatusInfoV2 cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED,
                             pending_bpids, (String) null, null);
                     TaggedAttribute tagattr = new TaggedAttribute(
                             new INTEGER(bpid++),
-                            OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                            OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                     controlSeq.addElement(tagattr);
                 }
 
@@ -959,7 +983,7 @@ public class CMCOutputTemplate {
 
         if (attr != null) {
             INTEGER attrbpid = attr.getBodyPartID();
-            CMCStatusInfo cmcStatusInfo = null;
+            CMCStatusInfoV2 cmcStatusInfoV2 = null;
             SET vals = attr.getValues();
             if (vals.size() > 0) {
                 RevokeRequest revRequest = (RevokeRequest) (ASN1Util.decode(new RevokeRequest.Template(),
@@ -988,14 +1012,14 @@ public class CMCOutputTemplate {
                                 CMS.debug(method + "missing CMC signer principal");
                                 OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
                                         new INTEGER(OtherInfo.BAD_MESSAGE_CHECK),
-                                        null);
+                                        null, null);
                                 SEQUENCE failed_bpids = new SEQUENCE();
                                 failed_bpids.addElement(attrbpid);
-                                cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null,
+                                cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null,
                                         otherInfo);
                                 tagattr = new TaggedAttribute(
                                         new INTEGER(bpid++),
-                                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                                 controlSeq.addElement(tagattr);
                                 return bpid;
                             }
@@ -1021,15 +1045,15 @@ public class CMCOutputTemplate {
                                     if (!verifyRevRequestSignature(msgData)) {
                                         OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL,
                                                 new INTEGER(OtherInfo.BAD_MESSAGE_CHECK),
-                                                null);
+                                                null, null);
                                         SEQUENCE failed_bpids = new SEQUENCE();
                                         failed_bpids.addElement(attrbpid);
-                                        cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids,
+                                        cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids,
                                                 (String) null,
                                                 otherInfo);
                                         tagattr = new TaggedAttribute(
                                                 new INTEGER(bpid++),
-                                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                                         controlSeq.addElement(tagattr);
                                         return bpid;
                                     }
@@ -1051,13 +1075,13 @@ public class CMCOutputTemplate {
                     if (tokenClass == null) {
                         CMS.debug(method + " Failed to retrieve shared secret plugin class");
                         OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.INTERNAL_CA_ERROR),
-                                null);
+                                null, null);
                         SEQUENCE failed_bpids = new SEQUENCE();
                         failed_bpids.addElement(attrbpid);
-                        cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null, otherInfo);
+                        cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null, otherInfo);
                         tagattr = new TaggedAttribute(
                                 new INTEGER(bpid++),
-                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                         controlSeq.addElement(tagattr);
                         return bpid;
                     }
@@ -1067,14 +1091,14 @@ public class CMCOutputTemplate {
 
                     if (sharedSecret == null) {
                         CMS.debug("CMCOutputTemplate: shared secret not found.");
-                        OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.INTERNAL_CA_ERROR),
-                                null);
+                        OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_IDENTITY),
+                                null, null);
                         SEQUENCE failed_bpids = new SEQUENCE();
                         failed_bpids.addElement(attrbpid);
-                        cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null, otherInfo);
+                        cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null, otherInfo);
                         tagattr = new TaggedAttribute(
                                 new INTEGER(bpid++),
-                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                         controlSeq.addElement(tagattr);
                         return bpid;
                     }
@@ -1088,14 +1112,14 @@ public class CMCOutputTemplate {
                     } else {
                         CMS.debug(method
                                 + " Client and server shared secret are not the same, cannot revoke certificate.");
-                        OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_MESSAGE_CHECK),
-                                null);
+                        OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_IDENTITY),
+                                null, null);
                         SEQUENCE failed_bpids = new SEQUENCE();
                         failed_bpids.addElement(attrbpid);
-                        cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null, otherInfo);
+                        cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null, otherInfo);
                         tagattr = new TaggedAttribute(
                                 new INTEGER(bpid++),
-                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                         controlSeq.addElement(tagattr);
 
                         audit(new CertStatusChangeRequestProcessedEvent(
@@ -1123,13 +1147,13 @@ public class CMCOutputTemplate {
 
                     if (record == null) {
                         CMS.debug(method + " The certificate is not found");
-                        OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_CERT_ID), null);
+                        OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_CERT_ID), null, null);
                         SEQUENCE failed_bpids = new SEQUENCE();
                         failed_bpids.addElement(attrbpid);
-                        cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null, otherInfo);
+                        cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null, otherInfo);
                         tagattr = new TaggedAttribute(
                                 new INTEGER(bpid++),
-                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                         controlSeq.addElement(tagattr);
                         return bpid;
                     }
@@ -1138,11 +1162,11 @@ public class CMCOutputTemplate {
                         CMS.debug("CMCOutputTemplate: The certificate is already revoked.");
                         SEQUENCE success_bpids = new SEQUENCE();
                         success_bpids.addElement(attrbpid);
-                        cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.SUCCESS,
+                        cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.SUCCESS,
                                 success_bpids, (String) null, null);
                         tagattr = new TaggedAttribute(
                                 new INTEGER(bpid++),
-                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                         controlSeq.addElement(tagattr);
                         return bpid;
                     }
@@ -1159,14 +1183,14 @@ public class CMCOutputTemplate {
                             msg = "certificate principal and signer do not match";
                             CMS.debug(method + msg);
                             OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_IDENTITY),
-                                    null);
+                                    null, null);
                             SEQUENCE failed_bpids = new SEQUENCE();
                             failed_bpids.addElement(attrbpid);
-                            cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, msg,
+                            cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, msg,
                                     otherInfo);
                             tagattr = new TaggedAttribute(
                                     new INTEGER(bpid++),
-                                    OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                    OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                             controlSeq.addElement(tagattr);
 
                             audit(new CertStatusChangeRequestProcessedEvent(
@@ -1220,14 +1244,14 @@ public class CMCOutputTemplate {
                             CMS.debug("CMCOutputTemplate: revReq exception: " +
                                     revReq.getExtDataInString(IRequest.ERROR));
                             OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_REQUEST),
-                                    null);
+                                    null, null);
                             SEQUENCE failed_bpids = new SEQUENCE();
                             failed_bpids.addElement(attrbpid);
-                            cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null,
+                            cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null,
                                     otherInfo);
                             tagattr = new TaggedAttribute(
                                     new INTEGER(bpid++),
-                                    OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                                    OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                             controlSeq.addElement(tagattr);
 
                             audit(new CertStatusChangeRequestProcessedEvent(
@@ -1254,11 +1278,11 @@ public class CMCOutputTemplate {
                     CMS.debug(method + " Certificate revoked.");
                     SEQUENCE success_bpids = new SEQUENCE();
                     success_bpids.addElement(attrbpid);
-                    cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.SUCCESS,
+                    cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.SUCCESS,
                             success_bpids, (String) null, null);
                     tagattr = new TaggedAttribute(
                             new INTEGER(bpid++),
-                            OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                            OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                     controlSeq.addElement(tagattr);
 
                     auditApprovalStatus = RequestStatus.COMPLETE;
@@ -1272,13 +1296,13 @@ public class CMCOutputTemplate {
                             auditApprovalStatus));
                     return bpid;
                 } else {
-                    OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.BAD_MESSAGE_CHECK), null);
+                    OtherInfo otherInfo = new OtherInfo(OtherInfo.FAIL, new INTEGER(OtherInfo.INTERNAL_CA_ERROR), null, null);
                     SEQUENCE failed_bpids = new SEQUENCE();
                     failed_bpids.addElement(attrbpid);
-                    cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.FAILED, failed_bpids, (String) null, otherInfo);
+                    cmcStatusInfoV2 = new CMCStatusInfoV2(CMCStatusInfoV2.FAILED, failed_bpids, (String) null, otherInfo);
                     tagattr = new TaggedAttribute(
                             new INTEGER(bpid++),
-                            OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
+                            OBJECT_IDENTIFIER.id_cmc_statusInfoV2, cmcStatusInfoV2);
                     controlSeq.addElement(tagattr);
 
                     audit(new CertStatusChangeRequestProcessedEvent(
diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/GenPendingTemplateFiller.java b/base/server/cms/src/com/netscape/cms/servlet/common/GenPendingTemplateFiller.java
index 4578a98..cfd42ad 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/common/GenPendingTemplateFiller.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/common/GenPendingTemplateFiller.java
@@ -35,7 +35,7 @@ import org.mozilla.jss.asn1.SEQUENCE;
 import org.mozilla.jss.asn1.SET;
 import org.mozilla.jss.crypto.DigestAlgorithm;
 import org.mozilla.jss.crypto.SignatureAlgorithm;
-import org.mozilla.jss.pkix.cmc.CMCStatusInfo;
+import org.mozilla.jss.pkix.cmc.CMCStatusInfoV2;
 import org.mozilla.jss.pkix.cmc.OtherInfo;
 import org.mozilla.jss.pkix.cmc.PendInfo;
 import org.mozilla.jss.pkix.cmc.ResponseBody;
@@ -98,7 +98,7 @@ public class GenPendingTemplateFiller implements ICMSTemplateFiller {
             RequestId reqId = req.getRequestId();
 
             fixed.set(ICMSTemplateFiller.REQUEST_ID, reqId);
-            // set pendInfo, CMCStatusInfo
+            // set pendInfo, CMCStatusInfoV2
             IArgBlock httpParams = cmsReq.getHttpParams();
 
             if (doFullResponse(httpParams)) {
@@ -115,12 +115,12 @@ public class GenPendingTemplateFiller implements ICMSTemplateFiller {
                 for (int i = 0; i < reqIdArray.length; i++) {
                     bpids.addElement(new INTEGER(reqIdArray[i]));
                 }
-                CMCStatusInfo cmcStatusInfo = new
-                        CMCStatusInfo(CMCStatusInfo.PENDING, bpids,
+                CMCStatusInfoV2 cmcStatusInfo = new
+                        CMCStatusInfoV2(CMCStatusInfoV2.PENDING, bpids,
                                 (String) null, otherInfo);
                 TaggedAttribute ta = new TaggedAttribute(new
                         INTEGER(bpid++),
-                        OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo,
+                        OBJECT_IDENTIFIER.id_cmc_statusInfoV2,
                         cmcStatusInfo);
 
                 controlSeq.addElement(ta);
diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java
index 73195e9..d087162 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java
@@ -19,6 +19,7 @@ package com.netscape.cms.servlet.profile;
 
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.math.BigInteger;
 import java.security.cert.X509Certificate;
 import java.util.Enumeration;
 import java.util.Locale;
@@ -51,6 +52,11 @@ import com.netscape.certsrv.logging.ILogger;
 import com.netscape.certsrv.logging.event.AuthFailEvent;
 import com.netscape.certsrv.logging.event.AuthSuccessEvent;
 import com.netscape.certsrv.logging.event.CertRequestProcessedEvent;
+import com.netscape.certsrv.profile.ECMCBadIdentityException;
+import com.netscape.certsrv.profile.ECMCBadMessageCheckException;
+import com.netscape.certsrv.profile.ECMCBadRequestException;
+import com.netscape.certsrv.profile.ECMCPopFailedException;
+import com.netscape.certsrv.profile.ECMCPopRequiredException;
 import com.netscape.certsrv.profile.EDeferException;
 import com.netscape.certsrv.profile.EProfileException;
 import com.netscape.certsrv.profile.ERejectException;
@@ -502,8 +508,60 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
         }
         try {
             reqs = profile.createRequests(ctx, locale);
+        } catch (ECMCBadMessageCheckException e) {
+            CMS.debug("ProfileSubmitCMCServlet: after createRequests - " + e.toString());
+            CMCOutputTemplate template = new CMCOutputTemplate();
+            SEQUENCE seq = new SEQUENCE();
+            seq.addElement(new INTEGER(0));
+            UTF8String s = null;
+            try {
+                s = new UTF8String(e.toString());
+            } catch (Exception ee) {
+            }
+            template.createFullResponseWithFailedStatus(response, seq,
+                    OtherInfo.BAD_MESSAGE_CHECK, s);
+            return;
+        } catch (ECMCBadIdentityException e) {
+            CMS.debug("ProfileSubmitCMCServlet: after createRequests - " + e.toString());
+            CMCOutputTemplate template = new CMCOutputTemplate();
+            SEQUENCE seq = new SEQUENCE();
+            seq.addElement(new INTEGER(0));
+            UTF8String s = null;
+            try {
+                s = new UTF8String(e.toString());
+            } catch (Exception ee) {
+            }
+            template.createFullResponseWithFailedStatus(response, seq,
+                    OtherInfo.BAD_IDENTITY, s);
+            return;
+        } catch (ECMCPopFailedException e) {
+            CMS.debug("ProfileSubmitCMCServlet: after createRequests - " + e.toString());
+            CMCOutputTemplate template = new CMCOutputTemplate();
+            SEQUENCE seq = new SEQUENCE();
+            seq.addElement(new INTEGER(0));
+            UTF8String s = null;
+            try {
+                s = new UTF8String(e.toString());
+            } catch (Exception ee) {
+            }
+            template.createFullResponseWithFailedStatus(response, seq,
+                    OtherInfo.POP_FAILED, s);
+            return;
+        } catch (ECMCBadRequestException e) {
+            CMS.debug("ProfileSubmitCMCServlet: after createRequests - " + e.toString());
+            CMCOutputTemplate template = new CMCOutputTemplate();
+            SEQUENCE seq = new SEQUENCE();
+            seq.addElement(new INTEGER(0));
+            UTF8String s = null;
+            try {
+                s = new UTF8String(e.toString());
+            } catch (Exception ee) {
+            }
+            template.createFullResponseWithFailedStatus(response, seq,
+                    OtherInfo.BAD_REQUEST, s);
+            return;
         } catch (EProfileException e) {
-            CMS.debug("ProfileSubmitCMCServlet: createRequests " + e.toString());
+            CMS.debug("ProfileSubmitCMCServlet: after createRequests - " + e.toString());
             CMCOutputTemplate template = new CMCOutputTemplate();
             SEQUENCE seq = new SEQUENCE();
             seq.addElement(new INTEGER(0));
@@ -516,7 +574,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                     OtherInfo.INTERNAL_CA_ERROR, s);
             return;
         } catch (Throwable e) {
-            CMS.debug("ProfileSubmitCMCServlet: createRequests " + e.toString());
+            CMS.debug("ProfileSubmitCMCServlet: createRequests - " + e.toString());
             CMCOutputTemplate template = new CMCOutputTemplate();
             SEQUENCE seq = new SEQUENCE();
             seq.addElement(new INTEGER(0));
@@ -570,7 +628,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
         boolean isRevoke = false;
         if (reqs == null) {
             // handling DecryptedPOP request here
-            Integer reqID = (Integer) context.get("cmcDecryptedPopReqId");
+            BigInteger reqID = (BigInteger) context.get("cmcDecryptedPopReqId");
             if (reqID == null) {
                 CMS.debug("ProfileSubmitCMCServlet: revocation request");
                 isRevoke = true;
@@ -683,8 +741,21 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                 }
                 profile.populateInput(ctx, reqs[k]);
                 profile.populate(reqs[k]);
+            } catch (ECMCPopFailedException e) {
+                CMS.debug("ProfileSubmitCMCServlet: after populate - " + e.toString());
+                CMCOutputTemplate template = new CMCOutputTemplate();
+                SEQUENCE seq = new SEQUENCE();
+                seq.addElement(new INTEGER(0));
+                UTF8String s = null;
+                try {
+                    s = new UTF8String(e.toString());
+                } catch (Exception ee) {
+                }
+                template.createFullResponseWithFailedStatus(response, seq,
+                        OtherInfo.POP_FAILED, s);
+                return;
             } catch (EProfileException e) {
-                CMS.debug("ProfileSubmitCMCServlet: populate " + e.toString());
+                CMS.debug("ProfileSubmitCMCServlet: after populate - " + e.toString());
                 CMCOutputTemplate template = new CMCOutputTemplate();
                 SEQUENCE seq = new SEQUENCE();
                 seq.addElement(new INTEGER(0));
@@ -697,7 +768,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                         OtherInfo.BAD_REQUEST, s);
                 return;
             } catch (Throwable e) {
-                CMS.debug("ProfileSubmitCMCServlet: populate " + e.toString());
+                CMS.debug("ProfileSubmitCMCServlet: after populate - " + e.toString());
                 //  throw new IOException("Profile " + profileId +
                 //          " cannot populate");
                 CMCOutputTemplate template = new CMCOutputTemplate();
@@ -780,6 +851,21 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                     errorReason = CMS.getUserMessage(locale,
                                 "CMS_PROFILE_REJECTED",
                                 e.toString());
+                } catch (ECMCPopRequiredException e) {
+                    // return popRequired message to the user
+                    CMS.debug("ProfileSubmitCMCServlet: popRequired; set request to PENDING");
+                    reqs[k].setRequestStatus(RequestStatus.PENDING);
+                    // need to notify
+                    INotify notify = profile.getRequestQueue().getPendingNotify();
+                    if (notify != null) {
+                        notify.notify(reqs[k]);
+                    }
+
+                    CMS.debug("ProfileSubmitCMCServlet: submit " + e.toString());
+                    errorCode = "4";
+                    errorReason = CMS.getUserMessage(locale,
+                                "CMS_PROFILE_CMC_POP_REQUIRED",
+                                e.toString());
                 } catch (Throwable e) {
                     // return error to the user
                     CMS.debug("ProfileSubmitCMCServlet: submit " + e.toString());
diff --git a/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java b/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java
index 76700fe..5666c13 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java
@@ -48,7 +48,7 @@ import org.mozilla.jss.asn1.SEQUENCE;
 import org.mozilla.jss.asn1.SET;
 import org.mozilla.jss.crypto.DigestAlgorithm;
 import org.mozilla.jss.crypto.SignatureAlgorithm;
-import org.mozilla.jss.pkix.cmc.CMCStatusInfo;
+import org.mozilla.jss.pkix.cmc.CMCStatusInfoV2;
 import org.mozilla.jss.pkix.cmc.PKIData;
 import org.mozilla.jss.pkix.cmc.ResponseBody;
 import org.mozilla.jss.pkix.cmc.TaggedAttribute;
@@ -431,11 +431,11 @@ public class CheckRequest extends CMSServlet {
 
                                         if (bodyPartId != null)
                                             bpids.addElement(bodyPartId);
-                                        CMCStatusInfo cmcStatusInfo = new
-                                                CMCStatusInfo(CMCStatusInfo.SUCCESS, bpids);
+                                        CMCStatusInfoV2 cmcStatusInfo = new
+                                                CMCStatusInfoV2(CMCStatusInfoV2.SUCCESS, bpids);
                                         TaggedAttribute ta = new TaggedAttribute(new
                                                 INTEGER(bpid++),
-                                                OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo,
+                                                OBJECT_IDENTIFIER.id_cmc_statusInfoV2,
                                                 cmcStatusInfo);
 
                                         controlSeq.addElement(ta);
diff --git a/base/server/cmsbundle/src/UserMessages.properties b/base/server/cmsbundle/src/UserMessages.properties
index ff56465..ed2a620 100644
--- a/base/server/cmsbundle/src/UserMessages.properties
+++ b/base/server/cmsbundle/src/UserMessages.properties
@@ -801,6 +801,7 @@ CMS_PROFILE_SUBJDIR_EMPTY_ATTRVAL=Attribute value should not be empty
 CMS_PROFILE_CRL_DISTRIBUTION_POINTS=CRL Distribution Points
 CMS_PROFILE_REJECTED=Request {0} Rejected - {1}
 CMS_PROFILE_DEFERRED=Request Deferred - {0}
+CMS_PROFILE_CMC_POP_REQUIRED=Request Deferred due to missing POP - {0}
 CMS_PROFILE_INTERNAL_ERROR=Request {0} - Server Internal Error
 CMS_PROFILE_KEY_ID=Key ID
 CMS_PROFILE_NOT_OWNER=Not Profile Owner
-- 
1.8.3.1