From 472292a63ee4cae23a6810bcbf68339671edc3c9 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Fri, 10 Mar 2017 19:50:13 -0800
Subject: [PATCH] Bug 1419742: CMC RFE: provide Proof of Possession for
 encryption cert requests CMC encryptedPOP and decrypedPOP (Phase 1) also
 disable lraPOPwitness This patch implements the Proof of Possession for
 encryption only keys. This is a preliminary implementation with limitations.
 It does not support more than one request. ECC keys are untested. This
 version only uses default algorithms at some internal places. Not all
 limitations are listed here.

---
 base/ca/shared/conf/CS.cfg                         |   2 +-
 .../src/com/netscape/cmstools/CMCRequest.java      | 892 +++++++++++++++------
 .../src/com/netscape/cmstools/CMCResponse.java     |  26 +
 .../src/com/netscape/cmstools/CRMFPopClient.java   |   7 +
 .../netscape/cms/profile/common/EnrollProfile.java | 388 ++++++++-
 .../cms/servlet/common/CMCOutputTemplate.java      | 140 +++-
 .../servlet/profile/ProfileSubmitCMCServlet.java   | 102 ++-
 .../com/netscape/cmsutil/crypto/CryptoUtil.java    |  70 +-
 8 files changed, 1300 insertions(+), 327 deletions(-)

diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg
index 3beb45c..65c3842 100644
--- a/base/ca/shared/conf/CS.cfg
+++ b/base/ca/shared/conf/CS.cfg
@@ -731,7 +731,7 @@ ca.publish.rule.instance.LdapXCertRule.predicate=
 ca.publish.rule.instance.LdapXCertRule.publisher=LdapCrossCertPairPublisher
 ca.publish.rule.instance.LdapXCertRule.type=xcert
 cmc.cert.confirmRequired=false
-cmc.lraPopWitness.verify.allow=true
+cmc.lraPopWitness.verify.allow=false
 cmc.revokeCert.verify=true
 cmc.revokeCert.sharedSecret.class=com.netscape.cms.authentication.SharedSecret
 cmc.sharedSecret.class=com.netscape.cms.authentication.SharedSecret
diff --git a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
index bca569b..4485f47 100644
--- a/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
+++ b/base/java-tools/src/com/netscape/cmstools/CMCRequest.java
@@ -31,6 +31,8 @@ import java.io.PrintStream;
 import java.math.BigInteger;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.StringTokenizer;
 
@@ -50,22 +52,33 @@ import org.mozilla.jss.asn1.UTF8String;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.DigestAlgorithm;
 import org.mozilla.jss.crypto.ObjectNotFoundException;
+import org.mozilla.jss.crypto.PrivateKey;
 import org.mozilla.jss.crypto.SignatureAlgorithm;
+import org.mozilla.jss.crypto.SymmetricKey;
 import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.pkcs10.CertificationRequest;
 import org.mozilla.jss.pkix.cmc.CMCCertId;
+import org.mozilla.jss.pkix.cmc.CMCStatusInfo;
+import org.mozilla.jss.pkix.cmc.DecryptedPOP;
+import org.mozilla.jss.pkix.cmc.EncryptedPOP;
 import org.mozilla.jss.pkix.cmc.GetCert;
 import org.mozilla.jss.pkix.cmc.IdentityProofV2;
 import org.mozilla.jss.pkix.cmc.LraPopWitness;
+import org.mozilla.jss.pkix.cmc.OtherInfo;
 import org.mozilla.jss.pkix.cmc.OtherMsg;
 import org.mozilla.jss.pkix.cmc.PKIData;
+import org.mozilla.jss.pkix.cmc.PendInfo;
+import org.mozilla.jss.pkix.cmc.ResponseBody;
 import org.mozilla.jss.pkix.cmc.TaggedAttribute;
 import org.mozilla.jss.pkix.cmc.TaggedCertificationRequest;
 import org.mozilla.jss.pkix.cmc.TaggedRequest;
 import org.mozilla.jss.pkix.cmmf.RevRequest;
 import org.mozilla.jss.pkix.cms.ContentInfo;
 import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo;
+import org.mozilla.jss.pkix.cms.EncryptedContentInfo;
+import org.mozilla.jss.pkix.cms.EnvelopedData;
 import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber;
+import org.mozilla.jss.pkix.cms.RecipientInfo;
 import org.mozilla.jss.pkix.cms.SignedData;
 import org.mozilla.jss.pkix.cms.SignerIdentifier;
 import org.mozilla.jss.pkix.cms.SignerInfo;
@@ -129,33 +142,31 @@ public class CMCRequest {
 
         X509Certificate cert = getCertificate(tokenName, nickname);
         if (cert != null)
-            System.out.println("got signing cert");
+            System.out.println("getPrivateKey: got signing cert");
 
         return CryptoManager.getInstance().findPrivKeyByCert(cert);
     }
 
     /**
-     * getCMCBlob create and return the enrollment request.
-     * <P>
+     * signData signs the request PKIData
      *
      * @param signerCert the certificate of the authorized signer of the CMC revocation request.
      * @param nickname the nickname of the certificate inside the token.
-     * @param rValue CRMF/PKCS10 request.
-     * @param format either crmf or pkcs10
-     * @return the CMC enrollment request encoded in base64
+     * @param pkidata the request PKIData to be signed
+     *
+     * @return the SignedData
+     *
      */
-    static ContentInfo getCMCBlob(X509Certificate signerCert, String tokenName, String nickname,
-            String[] rValue, String format, CryptoManager manager, String transactionMgtEnable,
-            String transactionMgtId,
-            String identificationEnable, String identification,
-            String identityProofEnable, String identityProofSharedSecret,
-            String identityProofV2Enable, String identityProofV2SharedSecret,
-            String identityProofV2hashAlg, String identityProofV2macAlg,
-            SEQUENCE controlSeq, SEQUENCE otherMsgSeq, int bpid) {
-
-        System.out.println("in getCMCBlob");
+    static SignedData signData(
+            X509Certificate signerCert,
+            String tokenName,
+            String nickname,
+            CryptoManager manager,
+            PKIData pkidata) {
+        String method = "signData: ";
+        SignedData req = null;
+        System.out.println(method + "begins: ");
 
-        ContentInfo fullEnrollmentReq = null;
         try {
             java.security.PrivateKey privKey = null;
             SignerIdentifier si = null;
@@ -175,8 +186,122 @@ public class CMCRequest {
                     SignerIdentifier.ISSUER_AND_SERIALNUMBER, ias, null);
             privKey = getPrivateKey(tokenName, nickname);
             if (privKey != null)
-                System.out.println("getCMCBlob: got privKey");
+                System.out.println(method + " got signer privKey");
+
+            EncapsulatedContentInfo ci = new EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, pkidata);
+            DigestAlgorithm digestAlg = null;
+            SignatureAlgorithm signAlg = null;
+            org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = ((org.mozilla.jss.crypto.PrivateKey) privKey)
+                    .getType();
+            if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.RSA)) {
+                signAlg = SignatureAlgorithm.RSASignatureWithSHA256Digest;
+            } else if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.EC)) {
+                signAlg = SignatureAlgorithm.ECSignatureWithSHA256Digest;
+            } else {
+                System.out.println("Algorithm not supported");
+                return null;
+            }
+
+            MessageDigest SHADigest = null;
+
+            byte[] digest = null;
+            try {
+                SHADigest = MessageDigest.getInstance("SHA256");
+                digestAlg = DigestAlgorithm.SHA256;
+
+                ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+
+                pkidata.encode(ostream);
+                digest = SHADigest.digest(ostream.toByteArray());
+            } catch (NoSuchAlgorithmException e) {
+            }
+            System.out.println(method + "digest created for pkidata");
+
+            SignerInfo signInfo = new SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg,
+                    (org.mozilla.jss.crypto.PrivateKey) privKey);
+            SET signInfos = new SET();
+            signInfos.addElement(signInfo);
+
+            SET digestAlgs = new SET();
+
+            if (digestAlg != null) {
+                AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null);
+                digestAlgs.addElement(ai);
+            }
+
+            org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert);
+            SET certs = new SET();
+
+            for (int i = 0; i < agentChain.length; i++) {
+                ANY cert = new ANY(agentChain[i].getEncoded());
+                certs.addElement(cert);
+            }
+
+            req = new SignedData(digestAlgs, ci, certs, null, signInfos);
+            System.out.println(method + "signed request generated.");
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+
+        return req;
+    }
+
+    /**
+     * getCMCBlob create and return the enrollment request.
+     *
+     * @return the CMC enrollment request encoded in base64
+     *
+     */
+    static ContentInfo getCMCBlob(SignedData req) {
+        String method = "getCMCBlob: ";
+        System.out.println(method + "begins");
+        ContentInfo fullEnrollmentReq = new ContentInfo(req);
+        try {
+            ByteArrayOutputStream bs = new ByteArrayOutputStream();
+            PrintStream ps = new PrintStream(bs);
+
+            if (fullEnrollmentReq != null) {
+                ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+                fullEnrollmentReq.encode(os);
+                ps.print(Utils.base64encode(os.toByteArray()));
+            }
+            String asciiBASE64Blob = bs.toString();
+
+            System.out.println("");
+            System.out.println("The CMC enrollment request in base-64 encoded format:");
+            System.out.println("");
+            System.out.println(asciiBASE64Blob);
+        } catch (Exception e) {
+            System.out.println(method + " Exception:" + e.toString());
+            System.exit(1);
+        }
+        return fullEnrollmentReq;
+    }
+
+    /**
+     * createPKIData creates PKIData
+     *
+     * @param rValue CRMF/PKCS10 request.
+     * @param format either crmf or pkcs10
+     * @return request in PKIData
+     */
+    static PKIData createPKIData(
+            String[] rValue, String format, String transactionMgtEnable,
+            String transactionMgtId,
+            String identificationEnable, String identification,
+            String identityProofEnable, String identityProofSharedSecret,
+            String identityProofV2Enable, String witnessSharedSecret,
+            String identityProofV2hashAlg, String identityProofV2macAlg,
+            SEQUENCE controlSeq, SEQUENCE otherMsgSeq, int bpid) {
+
+        String method = "createPKIData: ";
 
+        System.out.println(method + "begins");
+        PKIData pkidata = null;
+
+        try {
             TaggedRequest trq = null;
             PKCS10 pkcs = null;
             CertReqMsg certReqMsg = null;
@@ -185,20 +310,18 @@ public class CMCRequest {
             SEQUENCE reqSequence = new SEQUENCE();
             try {
                 for (int k = 0; k < rValue.length; k++) {
-                    System.out.println("k="+ k);
+                    System.out.println("k=" + k);
                     String asciiBASE64Blob = rValue[k];
                     byte[] decodedBytes = Utils.base64decode(asciiBASE64Blob);
 
                     if (format.equals("crmf")) {
-                        System.out.println("getCMCBlob: format: crmf");
-                        ByteArrayInputStream reqBlob =
-                                new ByteArrayInputStream(decodedBytes);
+                        System.out.println(method + " format: crmf");
+                        ByteArrayInputStream reqBlob = new ByteArrayInputStream(decodedBytes);
                         SEQUENCE crmfMsgs = null;
                         try {
-                            crmfMsgs = (SEQUENCE) new SEQUENCE.OF_Template(new
-                                    CertReqMsg.Template()).decode(reqBlob);
+                            crmfMsgs = (SEQUENCE) new SEQUENCE.OF_Template(new CertReqMsg.Template()).decode(reqBlob);
                         } catch (InvalidBERException ee) {
-                            System.out.println("getCMCBlob: This is not a crmf request. Or this request has an error.");
+                            System.out.println(method + " This is not a crmf request. Or this request has an error.");
                             System.exit(1);
                         }
                         certReqMsg = (CertReqMsg) crmfMsgs.elementAt(0);
@@ -208,25 +331,24 @@ public class CMCRequest {
                         try {
                             pkcs = new PKCS10(decodedBytes, true);
                         } catch (Exception e2) {
-                            System.out.println("getCMCBlob: Excception:"+e2.toString());
+                            System.out.println(method + " Excception:" + e2.toString());
                             System.exit(1);
                         }
                         ByteArrayInputStream crInputStream = new ByteArrayInputStream(
                                 pkcs.toByteArray());
-                        CertificationRequest cr = (CertificationRequest)
-                                CertificationRequest.getTemplate().decode(crInputStream);
+                        CertificationRequest cr = (CertificationRequest) CertificationRequest.getTemplate()
+                                .decode(crInputStream);
                         TaggedCertificationRequest tcr = new TaggedCertificationRequest(
                                 new INTEGER(bpid++), cr);
-                        trq = new
-                                TaggedRequest(TaggedRequest.PKCS10, tcr, null);
+                        trq = new TaggedRequest(TaggedRequest.PKCS10, tcr, null);
                     } else {
-                        System.out.println("getCMCBlob: Unrecognized request format: " + format);
+                        System.out.println(method + " Unrecognized request format: " + format);
                         System.exit(1);
                     }
                     reqSequence.addElement(trq);
                 }
             } catch (Exception e) {
-                System.out.println("getCMCBlob: Exception:"+ e.toString());
+                System.out.println(method + " Exception:" + e.toString());
                 System.exit(1);
             }
 
@@ -242,7 +364,7 @@ public class CMCRequest {
             // if both, V2 takes precedence
             if (identityProofV2Enable.equals("true")) {
                 bpid = addIdentityProofV2Attr(bpid, controlSeq, reqSequence,
-                        identityProofV2SharedSecret,
+                        witnessSharedSecret,
                         (identificationEnable.equals("true")) ? identification : null,
                         identityProofV2hashAlg, identityProofV2macAlg);
             } else if (identityProofEnable.equals("true")) {
@@ -250,77 +372,12 @@ public class CMCRequest {
                         identityProofSharedSecret);
             }
 
-            PKIData pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq);
-
-            EncapsulatedContentInfo ci = new
-                    EncapsulatedContentInfo(OBJECT_IDENTIFIER.id_cct_PKIData, pkidata);
-            // SHA1 is the default digest Alg for now.
-            DigestAlgorithm digestAlg = null;
-            SignatureAlgorithm signAlg = null;
-            org.mozilla.jss.crypto.PrivateKey.Type signingKeyType = ((org.mozilla.jss.crypto.PrivateKey) privKey).getType();
-            if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.RSA)) {
-                signAlg = SignatureAlgorithm.RSASignatureWithSHA1Digest;
-            } else if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.EC)) {
-                signAlg = SignatureAlgorithm.ECSignatureWithSHA1Digest;
-            } else if (signingKeyType.equals(org.mozilla.jss.crypto.PrivateKey.Type.DSA)) {
-                signAlg = SignatureAlgorithm.DSASignatureWithSHA1Digest;
-            }
-
-            MessageDigest SHADigest = null;
-
-            byte[] digest = null;
-            try {
-                SHADigest = MessageDigest.getInstance("SHA1");
-                digestAlg = DigestAlgorithm.SHA1;
-
-                ByteArrayOutputStream ostream = new ByteArrayOutputStream();
-
-                pkidata.encode(ostream);
-                digest = SHADigest.digest(ostream.toByteArray());
-            } catch (NoSuchAlgorithmException e) {
-            }
-            SignerInfo signInfo = new
-                    SignerInfo(si, null, null, OBJECT_IDENTIFIER.id_cct_PKIData, digest, signAlg,
-                            (org.mozilla.jss.crypto.PrivateKey) privKey);
-            SET signInfos = new SET();
-            signInfos.addElement(signInfo);
-
-            SET digestAlgs = new SET();
-
-            if (digestAlg != null) {
-                AlgorithmIdentifier ai = new AlgorithmIdentifier(digestAlg.toOID(), null);
-                digestAlgs.addElement(ai);
-            }
-
-            org.mozilla.jss.crypto.X509Certificate[] agentChain = manager.buildCertificateChain(signerCert);
-            SET certs = new SET();
-
-            for (int i = 0; i < agentChain.length; i++) {
-                ANY cert = new ANY(agentChain[i].getEncoded());
-                certs.addElement(cert);
-            }
-            SignedData req = new SignedData(digestAlgs, ci, certs, null, signInfos);
-            fullEnrollmentReq = new ContentInfo(req);
-            ByteArrayOutputStream bs = new ByteArrayOutputStream();
-            PrintStream ps = new PrintStream(bs);
-
-            if (fullEnrollmentReq != null) {
-                ByteArrayOutputStream os = new ByteArrayOutputStream();
-
-                fullEnrollmentReq.encode(os);
-                ps.print(Utils.base64encode(os.toByteArray()));
-            }
-            String asciiBASE64Blob = bs.toString();
-
-            System.out.println("");
-            System.out.println("The CMC enrollment request in base-64 encoded format:");
-            System.out.println("");
-            System.out.println(asciiBASE64Blob);
+            pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq);
         } catch (Exception e) {
             e.printStackTrace();
             System.exit(1);
         }
-        return fullEnrollmentReq;
+        return pkidata;
     }
 
     static void printUsage() {
@@ -330,16 +387,29 @@ public class CMCRequest {
         System.out.println("");
         System.out.println("The configuration file should look like as follows:");
         System.out.println("");
+        System.out.println("#decryptedPop.enable: if true, this is in response to an encryptedPOP request from the server from previous request;");
+        System.out.println("#    most all options other than the following are ignored:");
+        System.out.println("# encryptedPopResponseFile, privKeyId, decryptedPopRequestFile");
+        System.out.println("# where");
+        System.out.println("#   encryptPopResponse was the output from previous request, and is now an input to the new request that is about to be generated");
+        System.out.println("#   decryptedPopRequestFile is the output that is to be sent to the server as 2nd trip request in response to encryptedPOP");
+        System.out.println("#   privKeyId: used for decrypting the encryptedPOP and creating decryptedPOP");
+        System.out.println("");
+        System.out.println("decryptedPop.enable=false");
+        System.out.println("encryptedPopResponseFile=cmc.resp");
+        System.out.println("request.privKeyId=");
+        System.out.println("decryptedPopRequestFile=cmc.decreyptedPOP.req");
+        System.out.println("");
         System.out.println("#numRequests: Total number of PKCS10 requests or CRMF requests.");
         System.out.println("numRequests=1");
         System.out.println("");
         System.out.println("#input: full path for the PKCS10 request or CRMF request,");
         System.out.println("#the content must be in Base-64 encoded format");
         System.out.println("#Multiple files are supported. They must be separated by space.");
-        System.out.println("input=crmf1");
+        System.out.println("input=crmf.req");
         System.out.println("");
         System.out.println("#output: full path for the CMC request in binary format");
-        System.out.println("output=/u/doc/cmcReq");
+        System.out.println("output=cmc.req");
         System.out.println("");
         System.out.println("#tokenname: name of token where agent signing cert can be found (default is internal)");
         System.out.println("tokenname=internal");
@@ -349,7 +419,7 @@ public class CMCRequest {
         System.out.println("nickname=CMS Agent Certificate");
         System.out.println("");
         System.out.println("#dbdir: directory for cert8.db, key3.db and secmod.db");
-        System.out.println("dbdir=/u/smith/.netscape");
+        System.out.println("dbdir=./");
         System.out.println("");
         System.out.println("#password: password for cert8.db which stores the agent");
         System.out.println("#certificate");
@@ -360,7 +430,7 @@ public class CMCRequest {
         System.out.println("");
         System.out.println("#confirmCertAcceptance.enable: if true, then the request will");
         System.out.println("#contain this control. Otherwise, false.");
-        System.out.println("confirmCertAcceptance.enable=true");
+        System.out.println("confirmCertAcceptance.enable=false");
         System.out.println("");
         System.out.println("#confirmCertAcceptance.serial: The serial number for");
         System.out.println("#confirmCertAcceptance control");
@@ -372,7 +442,7 @@ public class CMCRequest {
         System.out.println("");
         System.out.println("#getCert.enable: if true, then the request will contain this");
         System.out.println("#control. Otherwise, false.");
-        System.out.println("getCert.enable=true");
+        System.out.println("getCert.enable=false");
         System.out.println("");
         System.out.println("#getCert.serial: The serial number for getCert control");
         System.out.println("getCert.serial=3");
@@ -382,14 +452,14 @@ public class CMCRequest {
         System.out.println("");
         System.out.println("#dataReturn.enable: if true, then the request will contain");
         System.out.println("#this control. Otherwise, false.");
-        System.out.println("dataReturn.enable=true");
+        System.out.println("dataReturn.enable=false");
         System.out.println("");
         System.out.println("#dataReturn.data: data contained in the control.");
         System.out.println("dataReturn.data=test");
         System.out.println("");
         System.out.println("#transactionMgt.enable: if true, then the request will contain");
         System.out.println("#this control. Otherwise, false.");
-        System.out.println("transactionMgt.enable=true");
+        System.out.println("transactionMgt.enable=false");
         System.out.println("");
         System.out.println("#transactionMgt.id: transaction identifier. Verisign recommend");
         System.out.println("#transactionId to be MD5 hash of publicKey.");
@@ -397,14 +467,14 @@ public class CMCRequest {
         System.out.println("");
         System.out.println("#senderNonce.enable: if true, then the request will contain this");
         System.out.println("#control. Otherwise, false.");
-        System.out.println("senderNonce.enable=true");
+        System.out.println("senderNonce.enable=false");
         System.out.println("");
         System.out.println("#senderNonce.id: sender nonce");
         System.out.println("senderNonce.id=");
         System.out.println("");
         System.out.println("#revRequest.enable: if true, then the request will contain this");
         System.out.println("#control. Otherwise, false.");
-        System.out.println("revRequest.enable=true");
+        System.out.println("revRequest.enable=false");
         System.out.println("");
         System.out.println("#revRequest.nickname: The nickname for the revoke certificate");
         System.out.println("revRequest.nickname=newuser's 102504a ID");
@@ -439,15 +509,15 @@ public class CMCRequest {
         System.out.println("#Note that if both identityProof and identityProofV2");
         System.out.println("#  are enabled, identityProofV2 takes precedence; Only one of them can be active at a time");
         System.out.println("#Supported hashAlg are:");
-        System.out.println("# SHA-1, SHA-256, SHA-384, and SHA-512");
+        System.out.println("# SHA-256, SHA-384, and SHA-512");
         System.out.println("#Supported macAlg are:");
-        System.out.println("# SHA-1-HMAC, SHA-256-HMAC, SHA-384-HMAC, and SHA-512-HMAC");
+        System.out.println("# SHA-256-HMAC, SHA-384-HMAC, and SHA-512-HMAC");
         System.out.println("identityProofV2.enable=false");
         System.out.println("identityProofV2.hashAlg=SHA-256");
         System.out.println("identityProofV2.macAlg=SHA-256-HMAC");
         System.out.println("");
-        System.out.println("#identityProofV2.sharedSecret: Shared Secret");
-        System.out.println("identityProofV2.sharedSecret=testing");
+        System.out.println("#witness.sharedSecret: Shared Secret");
+        System.out.println("witness.sharedSecret=testing");
         System.out.println("");
         System.out.println("#identification works with identityProofV2");
         System.out.println("identification.enable=false");
@@ -471,7 +541,7 @@ public class CMCRequest {
         System.out.println("");
         System.out.println("#LraPopWitness.enable: if true, then the request will contain this");
         System.out.println("#control. Otherwise, false.");
-        System.out.println("LraPopWitness.enable=true");
+        System.out.println("LraPopWitness.enable=false");
         System.out.println("");
         System.out.println("#LraPopWitness.bodyPartIDs: List of body part IDs");
         System.out.println("#Each id is separated by space.");
@@ -550,7 +620,7 @@ public class CMCRequest {
      * @param sharedSecret shared secret
      * @param hashAlgString hash algorithm
      * @param macAlgString mac algorithm
-     * cfu
+     * @author cfu
      */
     private static int addIdentityProofV2Attr(int bpid,
             SEQUENCE seq, SEQUENCE reqSequence,
@@ -926,7 +996,7 @@ public class CMCRequest {
      * @param seq
      * @param ident
      * @return
-     * cfu
+     * @author cfu
      */
     private static int addIdentificationAttr(int bpid, SEQUENCE seq, String ident) {
         UTF8String ident_s = null;
@@ -973,11 +1043,273 @@ public class CMCRequest {
         return bpid;
     }
 
+    /**
+     * processEncryptedPopResponse parses previous CMC response
+     * and returns the encryptedPop
+     *
+     * @param prevResponse file
+     * @param privKey
+     * @return encryptedPop and reqIdString 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 method = "processEncryptedPopResponse: ";
+        System.out.println(method + " begins.");
+
+        byte[] bb = new byte[10000];
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(prevResponse);
+            while (fis.available() > 0)
+                fis.read(bb, 0, 10000);
+        } catch (Exception e) {
+            System.out.println(method + "Error reading the response. Exception: " + e.toString());
+            System.exit(1);
+        }
+        System.out.println(method + " previous response read.");
+
+        org.mozilla.jss.pkix.cms.SignedData cmcFullResp = null;
+        try {
+            ByteArrayInputStream bis = new ByteArrayInputStream(bb);
+            org.mozilla.jss.pkix.cms.ContentInfo cii = (org.mozilla.jss.pkix.cms.ContentInfo) org.mozilla.jss.pkix.cms.ContentInfo
+                    .getTemplate().decode(bis);
+
+            cmcFullResp = (org.mozilla.jss.pkix.cms.SignedData) cii.getInterpretedContent();
+            EncapsulatedContentInfo ci = cmcFullResp.getContentInfo();
+            OBJECT_IDENTIFIER id = ci.getContentType();
+            OBJECT_IDENTIFIER dataid = new OBJECT_IDENTIFIER("1.2.840.113549.1.7.1");
+            if (!id.equals(OBJECT_IDENTIFIER.id_cct_PKIResponse) && !id.equals(dataid)) {
+                System.out.println(method + "Invalid CMC Response Format");
+            }
+
+            if (!ci.hasContent())
+                return null;
+
+            OCTET_STRING content1 = ci.getContent();
+            ByteArrayInputStream bbis = new ByteArrayInputStream(content1.toByteArray());
+            ResponseBody responseBody = (ResponseBody) (new ResponseBody.Template()).decode(bbis);
+            SEQUENCE controlSequence = responseBody.getControlSequence();
+            int numControls = controlSequence.size();
+            System.out.println(method + "Number of controls is " + numControls);
+
+            for (int i = 0; i < numControls; i++) {
+                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");
+                    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(),
+                                ASN1Util.encode(sts.elementAt(j)));
+                        SEQUENCE seq = cst.getBodyList();
+                        StringBuilder s = new StringBuilder("   BodyList: ");
+                        for (int k = 0; k < seq.size(); k++) {
+                            INTEGER n = (INTEGER) seq.elementAt(k);
+                            s.append(n.toString() + " ");
+                        }
+                        System.out.println(method + s);
+                        int st = cst.getStatus();
+                        if (st != CMCStatusInfo.SUCCESS && st != CMCStatusInfo.CONFIRM_REQUIRED) {
+                            String stString = cst.getStatusString();
+                            if (stString != null)
+                                System.out.println(method + "   Status String: " + stString);
+                            OtherInfo oi = cst.getOtherInfo();
+                            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);
+                            } else if (t == OtherInfo.PEND) {
+                                System.out.println(method + "   OtherInfo type: PEND");
+                                PendInfo pi = oi.getPendInfo();
+                                if (pi == null) {
+                                    System.out.println(method + "PendInfo null...skipping");
+                                    continue;
+                                } else
+                                    System.out.println(method + "PendInfo present...processing...");
+                                if (pi.getPendTime() != null) {
+                                    String datePattern = "dd/MMM/yyyy:HH:mm:ss z";
+                                    SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
+                                    Date d = pi.getPendTime().toDate();
+                                    System.out.println(method + "   Date: " + dateFormat.format(d));
+                                }
+                                OCTET_STRING pendToken = pi.getPendToken();
+                                if (pendToken != null) {
+                                    byte reqId[] = pendToken.toByteArray();
+                                    reqIdString = new String(reqId);
+                                    System.out.println(method + "   Pending request id: " + reqIdString);
+                                } else {
+                                    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) {
+                            System.out.println(method + "   Status: SUCCESS");
+                            System.out.println(
+                                    method + " not what we expected, because encryptedPOP.enable is true!!!! exit now");
+                            System.exit(1);
+                        }
+                    }
+                } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_encryptedPOP)) {
+                    // bingo
+                    System.out.println(method + "Control #" + i + ": CMC encrypted POP");
+                    System.out.println(method + "   OID: " + type.toString());
+                    SET encryptedPOPvals = taggedAttr.getValues();
+
+                    encryptedPop = (EncryptedPOP) (ASN1Util.decode(EncryptedPOP.getTemplate(),
+                            ASN1Util.encode(encryptedPOPvals.elementAt(0))));
+                    System.out.println(method + "encryptedPOP decoded successfully");
+
+                } // we don't expect any other controls
+            } //for
+        } catch (Exception e) {
+            System.out.println(method + e);
+            System.exit(1);
+        }
+
+        System.out.println(method + "ends");
+        return new Object[] { encryptedPop, reqIdString };
+    }
+
+    /**
+     * constructDecryptedPopRequest constructs request PKIData for DecryptedPOP
+     *
+     * @param encryptedPopInfo {EncryptedPOP, reqIdString}
+     * @param privKey
+     * @return request PKIData
+     * @author cfu
+     */
+    private static PKIData constructDecryptedPopRequest(
+            Object[] encryptedPopInfo,
+            String tokenName,
+            PrivateKey privKey) {
+        PKIData pkidata = null;
+        DecryptedPOP decryptedPop = null;
+
+        String method = "constructDecryptedPopRequest: ";
+        System.out.println(method + "begins");
+        if ((encryptedPopInfo == null) || (privKey == null)) {
+            System.out.println(method + "input params encryptedPopInfo and privKey cannot be null");
+            System.exit(1);
+        }
+
+        EncryptedPOP encryptedPop = (EncryptedPOP) encryptedPopInfo[0];
+        String reqIdString = (String) encryptedPopInfo[1];
+        if ((encryptedPop == null) || (reqIdString == null)) {
+            System.out.println(method + "encryptedPopInfo content encryptedPop and reqIdString cannot be null");
+            System.exit(1);
+        }
+
+        try {
+            TaggedRequest request = encryptedPop.getRequest();
+            AlgorithmIdentifier thePOPAlgID = encryptedPop.getThePOPAlgID();
+            AlgorithmIdentifier witnessAlgID = encryptedPop.getWitnessAlgID();
+            OCTET_STRING witness = encryptedPop.getWitness();
+            ContentInfo cms = encryptedPop.getContentInfo();
+            EnvelopedData envData = (EnvelopedData) cms.getInterpretedContent();
+            EncryptedContentInfo encCI = envData.getEncryptedContentInfo();
+            SET recipients = envData.getRecipientInfos();
+            RecipientInfo recipient = (RecipientInfo) (ASN1Util.decode(RecipientInfo.getTemplate(),
+                    ASN1Util.encode(recipients.elementAt(0))));
+            System.out.println(method + " previous response parsed.");
+
+            CryptoToken token = CryptoUtil.getKeyStorageToken(tokenName);
+            SymmetricKey symKey = CryptoUtil.unwrap(
+                    token,
+                    SymmetricKey.Usage.DECRYPT,
+                    privKey,
+                    recipient.getEncryptedKey().toByteArray());
+            if (symKey == null) {
+                System.out.println(method + "symKey returned null from CryptoUtil.unwrap(). Abort!");
+                System.exit(1);
+            }
+            System.out.println(method + "symKey unwrapped.");
+
+            byte challenge[] = CryptoUtil.decryptUsingSymmetricKey(
+                    token,
+                    encCI.getEncryptedContent().toByteArray(),
+                    symKey);
+            if (challenge == null) {
+                System.out
+                        .println(method + "challenge returned null from CryptoUtil.decryptUsingSymmetricKey(). Abort!");
+                System.exit(1);
+            }
+            System.out.println(method + "challenge decrypted.");
+
+            // now verify the witness
+            try {
+                MessageDigest hash = MessageDigest.getInstance(CryptoUtil.getNameFromHashAlgorithm(witnessAlgID));
+                byte[] digest = hash.digest(challenge);
+                boolean witnessChecked = Arrays.equals(digest, witness.toByteArray());
+                if (witnessChecked) {
+                    System.out.println(method + "Yay! witness verified");
+                } else {
+                    System.out.println(method + "Oops! witness failed to verify.  Must abort!");
+                    System.exit(1);
+                }
+            } catch (Exception ex) {
+                System.out.println(method + ex);
+                System.exit(1);
+            }
+
+            // now calculate the POP Proof Value
+            byte[] popProofValue = null;
+            try {
+                System.out.println(method + "calculating POP Proof Value");
+                MessageDigest SHA2Digest = MessageDigest.getInstance("SHA256");
+                HMACDigest hmacDigest = new HMACDigest(SHA2Digest, challenge);
+                hmacDigest.update(ASN1Util.encode(request));
+                popProofValue = hmacDigest.digest();
+            } catch (Exception ex) {
+                System.out.println(method + "calculating POP Proof Value failed: " + ex);
+                System.exit(1);
+            }
+
+            int bpid = 1;
+            // now construct DecryptedPOP
+            System.out.println(method + "constructing DecryptedPOP...");
+            decryptedPop = new DecryptedPOP(new INTEGER(reqIdString), 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++),
+                    OBJECT_IDENTIFIER.id_cmc_decryptedPOP,
+                    decryptedPop);
+
+            SEQUENCE reqSequence = new SEQUENCE();
+            reqSequence.addElement(request); //stuff original req
+
+            SEQUENCE controlSeq = new SEQUENCE();
+            controlSeq.addElement(decPop);
+            System.out.println(method + "decryptedPop control added");
+
+            SEQUENCE otherMsgSeq = new SEQUENCE();
+
+            pkidata = new PKIData(controlSeq, reqSequence, new SEQUENCE(), otherMsgSeq);
+        } catch (Exception e) {
+            System.out.println(method + e);
+            System.exit(1);
+        }
+
+        System.out.println(method + " completes.");
+        return pkidata;
+    }
+
     public static void main(String[] s) {
         String numRequests = null;
         String dbdir = null, nickname = null;
         String tokenName = null;
         String ifilename = null, ofilename = null, password = null, format = null;
+        String decryptedPopEnable = "false", encryptedPopResponseFile=null, privKeyId = null, decryptedPopRequestFile= null;
         String confirmCertEnable = "false", confirmCertIssuer = null, confirmCertSerial = null;
         String getCertEnable = "false", getCertIssuer = null, getCertSerial = null;
         String dataReturnEnable = "false", dataReturnData = null;
@@ -989,7 +1321,7 @@ public class CMCRequest {
         String revRequestInvalidityDatePresent = "false";
         String identificationEnable = "false", identification = null;
         String identityProofEnable = "false", identityProofSharedSecret = null;
-        String identityProofV2Enable = "false", identityProofV2SharedSecret = null, identityProofV2hashAlg = "SHA256", identityProofV2macAlg = "SHA256";
+        String identityProofV2Enable = "false", witnessSharedSecret = null, identityProofV2hashAlg = "SHA256", identityProofV2macAlg = "SHA256";
         String popLinkWitnessEnable = "false";
         String bodyPartIDs = null, lraPopWitnessEnable = "false";
 
@@ -1046,6 +1378,14 @@ public class CMCRequest {
                         ofilename = val;
                     } else if (name.equals("input")) {
                         ifilename = val;
+                    } else if (name.equals("decryptedPop.enable")) {
+                        decryptedPopEnable = val;
+                    } else if (name.equals("encryptedPopResponseFile")) {
+                        encryptedPopResponseFile = val;
+                    } else if (name.equals("request.privKeyId")) {
+                        privKeyId = val;
+                    } else if (name.equals("decryptedPopRequestFile")) {
+                        decryptedPopRequestFile = val;
                     } else if (name.equals("confirmCertAcceptance.serial")) {
                         confirmCertSerial = val;
                     } else if (name.equals("confirmCertAcceptance.issuer")) {
@@ -1092,8 +1432,8 @@ public class CMCRequest {
                         identification = val;
                     } else if (name.equals("identityProofV2.enable")) {
                         identityProofV2Enable = val;
-                    } else if (name.equals("identityProofV2.sharedSecret")) {
-                        identityProofV2SharedSecret = val;
+                    } else if (name.equals("witness.sharedSecret")) {
+                        witnessSharedSecret = val;
                     } else if (name.equals("identityProofV2.hashAlg")) {
                         identityProofV2hashAlg = val;
                     } else if (name.equals("identityProofV2.macAlg")) {
@@ -1118,45 +1458,6 @@ public class CMCRequest {
             printUsage();
         }
 
-        if (ifilename == null) {
-            System.out.println("Missing input filename for PKCS10 or CRMF.");
-            printUsage();
-        }
-
-        int num = 0;
-        if (numRequests == null) {
-            System.out.println("Missing numRequests.");
-            printUsage();
-        } else {
-            try {
-                num = Integer.parseInt(numRequests);
-            } catch (Exception ee) {
-                System.out.println("numRequests must be integer");
-                System.exit(1);
-            }
-        }
-
-        StringTokenizer tokenizer = new StringTokenizer(ifilename, " ");
-        String[] ifiles = new String[num];
-        for (int i = 0; i < num; i++) {
-            String ss = tokenizer.nextToken();
-            ifiles[i] = ss;
-            if (ss == null) {
-                System.out.println("Missing input file for the request.");
-                System.exit(1);
-            }
-        }
-
-        if (ofilename == null) {
-            System.out.println("Missing output filename for the CMC request.");
-            printUsage();
-        }
-
-        if (format == null) {
-            System.out.println("Missing format.");
-            printUsage();
-        }
-
         if (password == null) {
             System.out.println("Missing password.");
             printUsage();
@@ -1214,116 +1515,223 @@ public class CMCRequest {
                 System.out.println("got signerCert: "+ certname.toString());
             }
 
-            String[] requests = new String[num];
-            for (int i = 0; i < num; i++) {
-                BufferedReader inputBlob = null;
-                try {
-                    inputBlob = new BufferedReader(new InputStreamReader(
-                            new BufferedInputStream(new FileInputStream(ifiles[i]))));
-                } catch (FileNotFoundException e) {
-                    System.out.println("CMCRequest:  can't find file " +
-                            ifiles[i] + ":\n" + e);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    System.exit(1);
-                }
-                // (3) Read the entire contents of the specified BASE 64 encoded
-                //     blob into a String() object throwing away any
-                //     headers beginning with HEADER and any trailers beginning
-                //     with TRAILER
-                String asciiBASE64BlobChunk = "";
-                StringBuffer asciiBASE64Blob = new StringBuffer();
-
-                try {
-                    while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) {
-                        if (!(asciiBASE64BlobChunk.startsWith(HEADER)) &&
-                                !(asciiBASE64BlobChunk.startsWith(TRAILER))) {
-                            asciiBASE64Blob.append(asciiBASE64BlobChunk.trim());
-                        }
+            //cfu
+            ContentInfo cmcblob = null;
+            PKIData pkidata = null;
+            if (decryptedPopEnable.equalsIgnoreCase("true")) {
+                PrivateKey privk = null;
+                if (privKeyId == null) {
+                    System.out.println("ecryptedPop.enable = true, but privKeyId not specified.");
+                    printUsage();
+                } else {
+                    System.out.println("got privKeyId: " + privKeyId);
+
+                    byte[] keyIDb = CryptoUtil.string2byte(privKeyId);
+
+                    privk = CryptoUtil.findPrivateKeyFromID(keyIDb);
+
+                    if (privk != null) {
+                        System.out.println("got private key");
+                        // now we can use this to decrypt encryptedPOP
+                    } else {
+                        System.out.println("error getting private key null");
+                        System.exit(1);
                     }
-                    requests[i] = asciiBASE64Blob.toString();
-                } catch (IOException e) {
-                    System.out.println("CMCRequest:  Unexpected BASE64 " +
-                            "encoded error encountered in readLine():\n" +
-                            e);
                 }
-                // (4) Close the DataInputStream() object
-                try {
-                    inputBlob.close();
-                } catch (IOException e) {
-                    System.out.println("CMCRequest():  Unexpected BASE64 " +
-                            "encoded error encountered in close():\n" + e);
+
+                if (encryptedPopResponseFile == null) {
+                    System.out.println("ecryptedPop.enable = true, but encryptedPopResponseFile is not specified.");
+                    printUsage();
                 }
-            }
 
-            SEQUENCE controlSeq = new SEQUENCE();
-            int bpid = 1;
-            if (confirmCertEnable.equalsIgnoreCase("true")) {
-                if (confirmCertIssuer.length() == 0 || confirmCertSerial.length() == 0) {
-                    System.out.println("Illegal parameters for confirm certificate acceptance control");
+                if (decryptedPopRequestFile == null) {
+                    System.out.println("ecryptedPop.enable = true, but decryptedPopRequestFile is not specified.");
                     printUsage();
+                }
+                ofilename = decryptedPopRequestFile;
+
+                // now start processing decryptedPOP
+                Object[] encryptedPopInfo = processEncryptedPopResponse(encryptedPopResponseFile);
+                if (encryptedPopInfo == null) {
+                    System.out.println("processEncryptedPopResponse() returns null");
                     System.exit(1);
                 }
-                bpid = addConfirmCertAttr(bpid, controlSeq, confirmCertIssuer, confirmCertSerial);
-            }
+                pkidata = constructDecryptedPopRequest(encryptedPopInfo, tokenName, privk);
 
-            if (lraPopWitnessEnable.equalsIgnoreCase("true")) {
-                if (bodyPartIDs.length() == 0) {
-                    System.out.println("Illegal parameters for Lra Pop Witness control");
-                    printUsage();
+                if (pkidata == null) {
+                    System.out.println("after constructDecryptedPopRequest, pkidata null. no good");
                     System.exit(1);
                 }
+            } else { // !decryptedPopEnable
 
-                bpid = addLraPopWitnessAttr(bpid, controlSeq, bodyPartIDs);
-            }
+                if (ifilename == null) {
+                    System.out.println("Missing input filename for PKCS10 or CRMF.");
+                    printUsage();
+                }
 
-            if (getCertEnable.equalsIgnoreCase("true")) {
-                if (getCertIssuer.length() == 0 || getCertSerial.length() == 0) {
-                    System.out.println("Illegal parameters for get certificate control");
+                int num = 0;
+                if (numRequests == null) {
+                    System.out.println("Missing numRequests.");
                     printUsage();
-                    System.exit(1);
+                } else {
+                    try {
+                        num = Integer.parseInt(numRequests);
+                    } catch (Exception ee) {
+                        System.out.println("numRequests must be integer");
+                        System.exit(1);
+                    }
                 }
 
-                bpid = addGetCertAttr(bpid, controlSeq, getCertIssuer, getCertSerial);
-            }
+                StringTokenizer tokenizer = new StringTokenizer(ifilename, " ");
+                String[] ifiles = new String[num];
+                for (int i = 0; i < num; i++) {
+                    String ss = tokenizer.nextToken();
+                    ifiles[i] = ss;
+                    if (ss == null) {
+                        System.out.println("Missing input file for the request.");
+                        System.exit(1);
+                    }
+                }
 
-            if (dataReturnEnable.equalsIgnoreCase("true")) {
-                if (dataReturnData.length() == 0) {
-                    System.out.println("Illegal parameters for data return control");
+                if (ofilename == null) {
+                    System.out.println("Missing output filename for the CMC request.");
                     printUsage();
-                    System.exit(1);
                 }
 
-                bpid = addDataReturnAttr(bpid, controlSeq, dataReturnData);
-            }
+                if (format == null) {
+                    System.out.println("Missing format.");
+                    printUsage();
+                }
+                String[] requests = new String[num];
+                for (int i = 0; i < num; i++) {
+                    BufferedReader inputBlob = null;
+                    try {
+                        inputBlob = new BufferedReader(new InputStreamReader(
+                                new BufferedInputStream(new FileInputStream(ifiles[i]))));
+                    } catch (FileNotFoundException e) {
+                        System.out.println("CMCRequest:  can't find file " +
+                                ifiles[i] + ":\n" + e);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        System.exit(1);
+                    }
+                    // (3) Read the entire contents of the specified BASE 64 encoded
+                    //     blob into a String() object throwing away any
+                    //     headers beginning with HEADER and any trailers beginning
+                    //     with TRAILER
+                    String asciiBASE64BlobChunk = "";
+                    StringBuffer asciiBASE64Blob = new StringBuffer();
+
+                    try {
+                        while ((asciiBASE64BlobChunk = inputBlob.readLine()) != null) {
+                            if (!(asciiBASE64BlobChunk.startsWith(HEADER)) &&
+                                    !(asciiBASE64BlobChunk.startsWith(TRAILER))) {
+                                asciiBASE64Blob.append(asciiBASE64BlobChunk.trim());
+                            }
+                        }
+                        requests[i] = asciiBASE64Blob.toString();
+                    } catch (IOException e) {
+                        System.out.println("CMCRequest:  Unexpected BASE64 " +
+                                "encoded error encountered in readLine():\n" +
+                                e);
+                    }
+                    // (4) Close the DataInputStream() object
+                    try {
+                        inputBlob.close();
+                    } catch (IOException e) {
+                        System.out.println("CMCRequest():  Unexpected BASE64 " +
+                                "encoded error encountered in close():\n" + e);
+                    }
+                }
 
-            if (senderNonceEnable.equalsIgnoreCase("true"))
-                bpid = addSenderNonceAttr(bpid, controlSeq, senderNonce);
+                SEQUENCE controlSeq = new SEQUENCE();
+                int bpid = 1;
+                if (confirmCertEnable.equalsIgnoreCase("true")) {
+                    if (confirmCertIssuer.length() == 0 || confirmCertSerial.length() == 0) {
+                        System.out.println("Illegal parameters for confirm certificate acceptance control");
+                        printUsage();
+                        System.exit(1);
+                    }
+                    bpid = addConfirmCertAttr(bpid, controlSeq, confirmCertIssuer, confirmCertSerial);
+                }
 
-            if (popLinkWitnessEnable.equalsIgnoreCase("true"))
-                bpid = addPopLinkWitnessAttr(bpid, controlSeq);
+                if (lraPopWitnessEnable.equalsIgnoreCase("true")) {
+                    if (bodyPartIDs.length() == 0) {
+                        System.out.println("Illegal parameters for Lra Pop Witness control");
+                        printUsage();
+                        System.exit(1);
+                    }
 
-            SEQUENCE otherMsgSeq = new SEQUENCE();
-            if (revRequestEnable.equalsIgnoreCase("true")) {
-                if (revRequestIssuer.length() == 0 || revRequestSerial.length() == 0 ||
-                        revRequestReason.length() == 0) {
-                    System.out.println("Illegal parameters for revRequest control");
-                    printUsage();
-                    System.exit(1);
+                    bpid = addLraPopWitnessAttr(bpid, controlSeq, bodyPartIDs);
+                }
+
+                if (getCertEnable.equalsIgnoreCase("true")) {
+                    if (getCertIssuer.length() == 0 || getCertSerial.length() == 0) {
+                        System.out.println("Illegal parameters for get certificate control");
+                        printUsage();
+                        System.exit(1);
+                    }
+
+                    bpid = addGetCertAttr(bpid, controlSeq, getCertIssuer, getCertSerial);
                 }
 
-                bpid = addRevRequestAttr(bpid, controlSeq, otherMsgSeq, token, tokenName, revCertNickname,
-                        revRequestIssuer, revRequestSerial, revRequestReason, revRequestSharedSecret,
-                        revRequestComment, revRequestInvalidityDatePresent, cm);
+                if (dataReturnEnable.equalsIgnoreCase("true")) {
+                    if (dataReturnData.length() == 0) {
+                        System.out.println("Illegal parameters for data return control");
+                        printUsage();
+                        System.exit(1);
+                    }
+
+                    bpid = addDataReturnAttr(bpid, controlSeq, dataReturnData);
+                }
+
+                if (senderNonceEnable.equalsIgnoreCase("true"))
+                    bpid = addSenderNonceAttr(bpid, controlSeq, senderNonce);
+
+                if (popLinkWitnessEnable.equalsIgnoreCase("true"))
+                    bpid = addPopLinkWitnessAttr(bpid, controlSeq);
+
+                SEQUENCE otherMsgSeq = new SEQUENCE();
+                if (revRequestEnable.equalsIgnoreCase("true")) {
+                    if (revRequestIssuer.length() == 0 || revRequestSerial.length() == 0 ||
+                            revRequestReason.length() == 0) {
+                        System.out.println("Illegal parameters for revRequest control");
+                        printUsage();
+                        System.exit(1);
+                    }
+
+                    bpid = addRevRequestAttr(bpid, controlSeq, otherMsgSeq, token, tokenName, revCertNickname,
+                            revRequestIssuer, revRequestSerial, revRequestReason, revRequestSharedSecret,
+                            revRequestComment, revRequestInvalidityDatePresent, cm);
+                }
+
+                // create the request PKIData
+                pkidata = createPKIData(
+                        requests,
+                        format, transactionMgtEnable, transactionMgtId,
+                        identificationEnable, identification,
+                        identityProofEnable, identityProofSharedSecret,
+                        identityProofV2Enable, witnessSharedSecret,
+                        identityProofV2hashAlg, identityProofV2macAlg,
+                        controlSeq, otherMsgSeq, bpid);
+
+                if (pkidata == null) {
+                    System.out.println("pkidata null after createPKIData(). Exiting with error");
+                    System.exit(1);
+                }
             }
 
-            ContentInfo cmcblob = getCMCBlob(signerCert, tokenName, nickname, requests, format,
-                    cm, transactionMgtEnable, transactionMgtId,
-                    identificationEnable, identification,
-                    identityProofEnable, identityProofSharedSecret,
-                    identityProofV2Enable, identityProofV2SharedSecret,
-                    identityProofV2hashAlg, identityProofV2macAlg,
-                    controlSeq, otherMsgSeq, bpid);
+            // sign the request
+            SignedData signedData = signData(signerCert, tokenName, nickname, cm, pkidata);
+            if (signedData == null) {
+                System.out.println("signData() returns null. Exiting with error");
+                System.exit(1);
+            }
+            cmcblob = getCMCBlob(signedData);
+            if (cmcblob == null) {
+                System.out.println("getCMCBlob() returns null. Exiting with error");
+                System.exit(1);
+            }
 
             // (6) Finally, print the actual CMC blob to the
             //     specified output file
diff --git a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java
index 7bbba87..4c74934 100644
--- a/base/java-tools/src/com/netscape/cmstools/CMCResponse.java
+++ b/base/java-tools/src/com/netscape/cmstools/CMCResponse.java
@@ -34,6 +34,7 @@ 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.EncryptedPOP;
 import org.mozilla.jss.pkix.cmc.OtherInfo;
 import org.mozilla.jss.pkix.cmc.PendInfo;
 import org.mozilla.jss.pkix.cmc.ResponseBody;
@@ -140,12 +141,27 @@ public class CMCResponse {
                             else if (t == OtherInfo.PEND) {
                                 System.out.println("   OtherInfo type: PEND");
                                 PendInfo pi = oi.getPendInfo();
+                                if (pi == null) {
+                                    System.out.println("PendInfo null...skipping");
+                                    continue;
+                                } else
+                                    System.out.println("PendInfo present...processing...");
                                 if (pi.getPendTime() != null) {
                                     String datePattern = "dd/MMM/yyyy:HH:mm:ss z";
                                     SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
                                     Date d = pi.getPendTime().toDate();
                                     System.out.println("   Date: " + dateFormat.format(d));
                                 }
+                                OCTET_STRING pendToken = pi.getPendToken();
+                                if (pendToken != null) {
+                                    byte reqId[] = pendToken.toByteArray();
+                                    String reqIdString = new String(reqId);
+                                    System.out.println("   Pending request id: " + reqIdString);
+                                } else {
+                                    System.out.println("pendToken not in response");
+                                    System.exit(1);
+                                }
+
                             }
                         } else if (st == CMCStatusInfo.SUCCESS) {
                             System.out.println("   Status: SUCCESS");
@@ -200,6 +216,16 @@ public class CMCResponse {
                         s.append(" ");
                     }
                     System.out.println(s);
+                } else if (type.equals(OBJECT_IDENTIFIER.id_cmc_encryptedPOP)) {
+                    System.out.println("Control #" + i + ": CMC encrypted POP");
+                    System.out.println("   OID: " + type.toString());
+                    SET encryptedPOPvals = taggedAttr.getValues();
+
+                    EncryptedPOP encryptedPOP =
+                        (EncryptedPOP) (ASN1Util.decode(EncryptedPOP.getTemplate(),
+                            ASN1Util.encode(encryptedPOPvals.elementAt(0))));
+                    System.out.println("after encryptedPOP encode");
+
                 }
             }
         } catch (Exception e) {
diff --git a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java
index 5e53bee..fafe956 100644
--- a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java
+++ b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java
@@ -433,6 +433,13 @@ public class CRMFPopClient {
                 throw new Exception("Unknown algorithm: " + algorithm);
             }
 
+            // print out keyid to be used in cmc decryptPOP
+            PrivateKey privateKey = (PrivateKey) keyPair.getPrivate();
+            @SuppressWarnings("deprecation")
+            byte id[] = privateKey.getUniqueID();
+            String kid = CryptoUtil.byte2string(id);
+            System.out.println("Keypair private key id: " + kid);
+
             if (verbose) System.out.println("Creating certificate request");
             CertRequest certRequest = client.createCertRequest(token, transportCert, algorithm, keyPair, subject);
 
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 972d89f..5c07ecb 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
@@ -24,10 +24,13 @@ import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
 import java.security.cert.CertificateException;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.Locale;
+import java.util.Random;
 import java.util.StringTokenizer;
 
 import org.mozilla.jss.CryptoManager;
@@ -43,8 +46,11 @@ import org.mozilla.jss.asn1.UTF8String;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.DigestAlgorithm;
 import org.mozilla.jss.crypto.HMACAlgorithm;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.crypto.SymmetricKey;
 import org.mozilla.jss.pkcs10.CertificationRequest;
 import org.mozilla.jss.pkcs10.CertificationRequestInfo;
+import org.mozilla.jss.pkix.cmc.DecryptedPOP;
 import org.mozilla.jss.pkix.cmc.IdentityProofV2;
 import org.mozilla.jss.pkix.cmc.LraPopWitness;
 import org.mozilla.jss.pkix.cmc.OtherMsg;
@@ -78,6 +84,7 @@ import com.netscape.certsrv.profile.IEnrollProfile;
 import com.netscape.certsrv.profile.IProfileContext;
 import com.netscape.certsrv.request.IRequest;
 import com.netscape.certsrv.request.IRequestQueue;
+import com.netscape.certsrv.request.RequestId;
 import com.netscape.cmsutil.crypto.CryptoUtil;
 import com.netscape.cmsutil.util.HMACDigest;
 
@@ -162,13 +169,19 @@ public abstract class EnrollProfile extends BasicProfile
 
             num_requests = msgs.length;
         }
+        TaggedRequest[] cmc_msgs = null;
         if (cert_request_type != null && cert_request_type.startsWith("cmc")) {
+            /*
+             * TODO: cfu: Phase 2: check if CMCAuth pre-signed request passed.
+             *     if not, identityProofV2 and/or identification controls
+             *     are required;
+             */
             // catch for invalid request
-            TaggedRequest[] msgs = parseCMC(locale, cert_request);
-            if (msgs == null)
+            cmc_msgs = parseCMC(locale, cert_request);
+            if (cmc_msgs == null)
                 return null;
             else
-                num_requests = msgs.length;
+                num_requests = cmc_msgs.length;
         }
 
         // only 1 request for renewal
@@ -191,6 +204,12 @@ public abstract class EnrollProfile extends BasicProfile
                 result[i].setExtData(REQUEST_SEQ_NUM, renewal_seq_num);
             } else {
                 result[i].setExtData(REQUEST_SEQ_NUM, Integer.valueOf(i));
+                if ((cmc_msgs != null) && (cmc_msgs[i] != null)) {
+                    CMS.debug("EnrollProfile: createRequests: setting cmc TaggedRequest in request");
+                    result[i].setExtData(
+                            CTX_CERT_REQUEST,
+                            ASN1Util.encode(cmc_msgs[i]));
+                }
             }
             if (locale != null) {
                 result[i].setExtData(REQUEST_LOCALE, locale.getLanguage());
@@ -325,6 +344,93 @@ public abstract class EnrollProfile extends BasicProfile
     }
 
     /**
+     * setPOPchallenge generates a POP challenge and sets necessary info in request
+     * for composing encryptedPOP later
+     *
+     * @param IRequest the request
+     * @author cfu
+     */
+    public void setPOPchallenge(IRequest req) throws EBaseException {
+        String method = "EnrollProfile: setPOPchallenge: ";
+
+        CMS.debug(method + " getting user public key in request");
+        byte[] req_key_data = req.getExtDataInByteArray(IEnrollProfile.REQUEST_KEY);
+        netscape.security.x509.CertificateX509Key pubKey = null;
+        if (req_key_data != null) {
+            CMS.debug(method + "found user public key in request");
+
+            // generate a challenge of 64 bytes;
+            Random random = new Random();
+            byte[] challenge = new byte[64];
+            random.nextBytes(challenge);
+
+            ICertificateAuthority authority = (ICertificateAuthority) getAuthority();
+            PublicKey issuanceProtPubKey = authority.getIssuanceProtPubKey();
+            if (issuanceProtPubKey != null)
+                CMS.debug(method + "issuanceProtPubKey not null");
+            else
+                CMS.debug(method + "issuanceProtPubKey null");
+
+            String msg = "";
+            try {
+                CryptoToken token = null;
+                String tokenName = CMS.getConfigStore().getString("cmc.token", CryptoUtil.INTERNAL_TOKEN_NAME);
+                token = CryptoUtil.getCryptoToken(tokenName);
+
+                PublicKey userPubKey = X509Key.parsePublicKey(new DerValue(req_key_data));
+
+                SymmetricKey symKey = CryptoUtil.generateKey(token);
+                byte[] pop_encreyptedData = CryptoUtil.encryptUsingSymmetricKey(
+                        token, symKey, challenge);
+                if (pop_encreyptedData == null) {
+                    msg = method + "pop_encreyptedData null";
+                    CMS.debug(msg);
+                    throw new Exception(msg);
+                }
+
+                byte[] pop_sysPubEncreyptedSession = CryptoUtil.wrapUsingPublicKey(
+                        token, issuanceProtPubKey, symKey);
+                if (pop_sysPubEncreyptedSession == null) {
+                    msg = method + "pop_sysPubEncreyptedSession null";
+                    CMS.debug(msg);
+                    throw new Exception(msg);
+                }
+
+                byte[] pop_userPubEncreyptedSession = CryptoUtil.wrapUsingPublicKey(
+                        token, userPubKey, symKey);
+                if (pop_userPubEncreyptedSession == null) {
+                    msg = method + "pop_userPubEncreyptedSession null";
+                    CMS.debug(msg);
+                    throw new Exception(msg);
+                }
+                CMS.debug(method + "POP challenge fields generated successfully...setting request extData");
+
+                req.setExtData("pop_encreyptedData", pop_encreyptedData);
+
+                req.setExtData("pop_sysPubEncreyptedSession", pop_sysPubEncreyptedSession);
+
+                req.setExtData("pop_userPubEncreyptedSession", pop_userPubEncreyptedSession);
+
+                // now compute and set witness
+                CMS.debug(method + "now compute and set witness");
+                String hashName = CryptoUtil.getDefaultHashAlgName();
+                CMS.debug(method + "hashName is " + hashName);
+                MessageDigest hash = MessageDigest.getInstance(hashName);
+                byte[] witness = hash.digest(challenge);
+                req.setExtData("pop_witness", witness);
+
+            } catch (Exception e) {
+                CMS.debug(method + e);
+                throw new EBaseException(e.toString());
+            }
+
+        } else {
+            CMS.debug(method + " public key not found in request");
+            throw new EBaseException(method + " public key not found in request");
+        }
+    }
+
+    /**
      * This method is called after the user submits the
      * request from the end-entity page.
      */
@@ -341,8 +447,14 @@ public abstract class EnrollProfile extends BasicProfile
         //     process request
         //   }
         // }
+        String method = "EnrollProfile: submit: ";
 
         IRequestQueue queue = getRequestQueue();
+        String msg = "";
+
+        boolean popChallengeRequired =
+                request.getExtDataInBoolean("cmc_POPchallengeRequired", false);
+        CMS.debug(method + "popChallengeRequired =" + popChallengeRequired);
 
         // this profile queues request that is authenticated
         // by NoAuth
@@ -350,26 +462,42 @@ public abstract class EnrollProfile extends BasicProfile
             queue.updateRequest(request);
         } catch (EBaseException e) {
             // save request to disk
-            CMS.debug("EnrollProfile: Unable to update request: " + e);
+            CMS.debug(method + " Unable to update request: " + e);
             CMS.debug(e);
         }
 
-        if (token == null) {
-            CMS.debug("EnrollProfile: auth token is null");
-            CMS.debug("EnrollProfile: validating request");
+        if (token == null){
+            CMS.debug(method + " auth token is null");
+            CMS.debug(method + " validating request");
             validate(request);
             try {
                 queue.updateRequest(request);
             } catch (EBaseException e) {
-                CMS.debug("EnrollProfile: Unable to update request after validation: " + e);
-                CMS.debug(e);
+                msg = method + " Unable to update request after validation: " + e;
+                CMS.debug(msg);
+                throw new EProfileException(msg);
             }
-
             throw new EDeferException("defer request");
+        } else if (popChallengeRequired) {
+            // this is encryptedPOP case; defer to require decryptedPOP
+            CMS.debug(method + " popChallengeRequired, defer to enforce decryptedPOP");
+            validate(request);
+            CMS.debug(method + " about to call setPOPchallenge");
+            try {
+                setPOPchallenge(request);
+                queue.updateRequest(request);
+            } catch (EBaseException e) {
+                msg = method + e;
+                CMS.debug(msg);
+                throw new EProfileException(msg);
+            }
+
+            throw new EDeferException("EnrollProfile: submit: encryptedPOP defer request");
+
         } else {
             // this profile executes request that is authenticated
             // by non NoAuth
-            CMS.debug("EnrollProfile: auth token is not null");
+            CMS.debug(method + " auth token is not null");
             validate(request);
             execute(request);
         }
@@ -377,14 +505,16 @@ public abstract class EnrollProfile extends BasicProfile
 
     public TaggedRequest[] parseCMC(Locale locale, String certreq)
             throws EProfileException {
+
+        String method = "EnrollProfile: parseCMC: ";
         /* cert request must not be null */
         if (certreq == null) {
-            CMS.debug("EnrollProfile: parseCMC() certreq null");
+            CMS.debug(method + "certreq null");
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"));
         }
-        //CMS.debug("EnrollProfile: Start parseCMC(): " + certreq);
-        CMS.debug("EnrollProfile.parseCMC(): start");
+        //CMS.debug(method + " Start parseCMC(): " + certreq);
+        CMS.debug(method + "starts");
 
         TaggedRequest msgs[] = null;
 
@@ -416,6 +546,8 @@ public abstract class EnrollProfile extends BasicProfile
                 if (numcontrols > 0) {
                     context.put("numOfControls", Integer.valueOf(numcontrols));
                     TaggedAttribute[] attributes = new TaggedAttribute[numcontrols];
+                    boolean id_cmc_decryptedPOP = false;
+                    SET decPopVals = null;
 
                     boolean id_cmc_identification = false;
                     SET ident = null;
@@ -430,7 +562,11 @@ public abstract class EnrollProfile extends BasicProfile
                     for (int i = 0; i < numcontrols; i++) {
                         attributes[i] = (TaggedAttribute) controlSeq.elementAt(i);
                         OBJECT_IDENTIFIER oid = attributes[i].getType();
-                        if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) {
+                        if (oid.equals(OBJECT_IDENTIFIER.id_cmc_decryptedPOP)) {
+                            CMS.debug(method + " decryptedPOP found");
+                            id_cmc_decryptedPOP = true;
+                            decPopVals = attributes[i].getValues();
+                        } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) {
                             id_cmc_identification = true;
                             ident = attributes[i].getValues();
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProofV2)) {
@@ -460,6 +596,19 @@ public abstract class EnrollProfile extends BasicProfile
                      */
 
                     UTF8String ident_s = null;
+                    if (id_cmc_decryptedPOP && (decPopVals != null)) {
+
+                        DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(),
+                                ASN1Util.encode(decPopVals.elementAt(0))));
+                        CMS.debug(method + "DecryptedPOP encoded");
+
+                        Integer reqId = verifyDecryptedPOP(decPop);
+                        if (reqId != null) {
+                            context.put("decryptedPopReqId", reqId);
+                        }
+                        return null;
+                    }
+
                     if (id_cmc_identification) {
                         ident_s = (UTF8String) (ASN1Util.decode(UTF8String.getTemplate(),
                                 ASN1Util.encode(ident.elementAt(0))));
@@ -523,16 +672,111 @@ public abstract class EnrollProfile extends BasicProfile
             } else
                 return null;
 
-            CMS.debug("EnrollProfile.parseCMC: end");
+            CMS.debug(method + "ends");
             return msgs;
         } catch (Exception e) {
-            CMS.debug("EnrollProfile: Unable to parse CMC request: " + e);
-            CMS.debug(e);
+            CMS.debug(method + "Unable to parse CMC request: " + e);
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e);
         }
     }
 
+    /**
+     * verifyDecryptedPOP verifies the POP challenge provided in
+     * DecryptedPOP and returns the matching requestID
+     *
+     * @author cfu
+     */
+    private Integer verifyDecryptedPOP(DecryptedPOP decPop) {
+        String method = "EnrollProfile: verifyDecryptedPOP: ";
+        CMS.debug(method + "begins");
+
+        INTEGER iBody = decPop.getBodyPartID();
+        CMS.debug(method + "request id from decryptedPOP =" +
+                iBody.toString());
+        Integer reqId = new Integer(iBody.toString());
+
+        OCTET_STRING witness_os = decPop.getWitness();
+
+        IRequestQueue reqQueue = getRequestQueue();
+        IRequest req = null;
+        try {
+            req = reqQueue.findRequest(new RequestId(reqId));
+        } catch (Exception e) {
+            CMS.debug(method + e);
+            return null;
+        }
+
+        // now verify the POP witness
+        byte[] pop_encreyptedData = req.getExtDataInByteArray("pop_encreyptedData");
+        if (pop_encreyptedData == null) {
+            CMS.debug(method +
+                    "pop_encreyptedData not found in request:" + reqId.toString());
+            return null;
+        }
+
+        byte[] pop_sysPubEncreyptedSession = req.getExtDataInByteArray("pop_sysPubEncreyptedSession");
+        if (pop_sysPubEncreyptedSession == null) {
+            CMS.debug(method +
+                    "pop_sysPubEncreyptedSession not found in request:" + reqId.toString());
+            return null;
+        }
+
+        byte[] cmc_msg = req.getExtDataInByteArray(IEnrollProfile.CTX_CERT_REQUEST);
+        if (pop_sysPubEncreyptedSession == null) {
+            CMS.debug(method +
+                    "pop_sysPubEncreyptedSession not found in request:" +
+                    reqId.toString());
+            return null;
+        }
+
+        ICertificateAuthority authority = (ICertificateAuthority) getAuthority();
+        PrivateKey issuanceProtPrivKey = authority.getIssuanceProtPrivKey();
+        if (issuanceProtPrivKey != null)
+            CMS.debug(method + "issuanceProtPrivKey not null");
+        else
+            CMS.debug(method + "issuanceProtPrivKey null");
+
+        try {
+            CryptoToken token = null;
+            String tokenName = CMS.getConfigStore().getString("cmc.token", CryptoUtil.INTERNAL_TOKEN_NAME);
+            token = CryptoUtil.getKeyStorageToken(tokenName);
+
+            SymmetricKey symKey = CryptoUtil.unwrap(
+                    token,
+                    SymmetricKey.Usage.DECRYPT,
+                    issuanceProtPrivKey,
+                    pop_sysPubEncreyptedSession);
+            byte[] challenge_b = CryptoUtil.decryptUsingSymmetricKey(
+                    token, pop_encreyptedData, symKey);
+            if (challenge_b == null) {
+                CMS.debug(method + "decryptUsingSymmetricKey returned null");
+                return null;
+            }
+
+            MessageDigest digest = MessageDigest.getInstance(CryptoUtil.getDefaultHashAlgName());
+            HMACDigest hmacDigest = new HMACDigest(digest, challenge_b);
+            hmacDigest.update(cmc_msg);
+            byte[] proofValue = hmacDigest.digest();
+            boolean witnessChecked = Arrays.equals(proofValue, witness_os.toByteArray());
+            if (!witnessChecked) {
+                CMS.debug(method + "POP challenge witness verification failure");
+                return null;
+            }
+        } catch (Exception e) {
+            CMS.debug(method + e);
+            return null;
+        }
+
+        CMS.debug(method + "POP challenge verified!");
+        req.setExtData("cmc_POPchallengeRequired", "false");
+
+        CMS.debug(method + "cmc_POPchallengeRequired set back to false");
+        CMS.debug(method + "ends");
+
+        return reqId;
+    }
+
     private boolean verifyPOPLinkWitness(byte[] randomSeed, TaggedRequest req,
             SEQUENCE bpids) {
         ISharedToken tokenClass = null;
@@ -711,7 +955,7 @@ public abstract class EnrollProfile extends BasicProfile
      * @param ident value of the id_cmc_identification control
      * @param reqSeq requestSequence of the PKI request PKIData
      * @return boolean true if the witness values correctly verified
-     * cfu
+     * @author cfu
      */
     private boolean verifyIdentityProofV2(
             TaggedAttribute attr,
@@ -769,8 +1013,8 @@ public abstract class EnrollProfile extends BasicProfile
             CMS.debug(method + " Failed to retrieve shared secret");
             return false;
         }
-        // cfu REMOVE
-        CMS.debug(method + "Shared Secret returned by tokenClass:" + token);
+
+        // CMS.debug(method + "Shared Secret returned by tokenClass:" + token);
         try {
             IdentityProofV2 idV2val = (IdentityProofV2) (ASN1Util.decode(IdentityProofV2.getTemplate(),
                     ASN1Util.encode(vals.elementAt(0))));
@@ -851,16 +1095,17 @@ public abstract class EnrollProfile extends BasicProfile
     public void fillTaggedRequest(Locale locale, TaggedRequest tagreq, X509CertInfo info,
             IRequest req)
             throws EProfileException {
+        String method = "EnrollProfile: fillTaggedRequest: ";
         TaggedRequest.Type type = tagreq.getType();
         if (type == null) {
-            CMS.debug("EnrollProfile: fillTaggedRequest: TaggedRequest type == null");
+            CMS.debug(method + "TaggedRequest type == null");
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST")+
                     "TaggedRequest type null");
         }
 
         if (type.equals(TaggedRequest.PKCS10)) {
-            CMS.debug("EnrollProfile: fillTaggedRequest: TaggedRequest type == pkcs10");
+            CMS.debug(method + " TaggedRequest type == pkcs10");
             boolean sigver = true;
             boolean tokenSwitched = false;
             CryptoManager cm = null;
@@ -890,36 +1135,73 @@ public abstract class EnrollProfile extends BasicProfile
                 req.setExtData("bodyPartId", tcr.getBodyPartID());
                 fillPKCS10(locale, pkcs10, info, req);
             } catch (Exception e) {
-                CMS.debug("EnrollProfile: fillTaggedRequest " + e);
+                CMS.debug(method + e);
             }  finally {
                 if ((sigver == true) && (tokenSwitched == true)){
                     cm.setThreadToken(savedToken);
                 }
             }
         } else if (type.equals(TaggedRequest.CRMF)) {
-            CMS.debug("EnrollProfile: fillTaggedRequest: TaggedRequest type == crmf");
+            CMS.debug(method + " TaggedRequest type == crmf");
             CertReqMsg crm = tagreq.getCrm();
             SessionContext context = SessionContext.getContext();
             Integer nums = (Integer) (context.get("numOfControls"));
 
-            // check if the LRA POP Witness Control attribute exists
-            if (nums != null && nums.intValue() > 0) {
-                TaggedAttribute attr =
-                        (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_lraPOPWitness));
-                if (attr != null) {
-                    parseLRAPopWitness(locale, crm, attr);
+            boolean verifyAllow = false; //disable RA by default
+            try {
+                String configName = "cmc.lraPopWitness.verify.allow";
+                CMS.debug(method + "getting :" + configName);
+                verifyAllow = CMS.getConfigStore().getBoolean(configName, false);
+                CMS.debug(method + "cmc.lraPopWitness.verify.allow is " + verifyAllow);
+            } catch (Exception e) {
+                // unlikely to get here
+                String msg = method + " Failed to retrieve cmc.lraPopWitness.verify.allow";
+                CMS.debug(msg);
+                throw new EProfileException(method + msg);
+            }
+            if (verifyAllow) {
+                // check if the LRA POP Witness Control attribute exists
+                if (nums != null && nums.intValue() > 0) {
+                    TaggedAttribute attr = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_lraPOPWitness));
+                    if (attr != null) {
+                        parseLRAPopWitness(locale, crm, attr);
+                    } else {
+                        CMS.debug(
+                                method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
+                        if (crm.hasPop()) {
+                            CMS.debug(method + " hasPop true");
+                            verifyPOP(locale, crm);
+                        } else { // no signing POP, then do it the hard way
+                            CMS.debug(method + "hasPop false, need to challenge");
+                            req.setExtData("cmc_POPchallengeRequired", "true");
+                        }
+                    }
                 } else {
-                    CMS.debug("EnrollProfile: verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
+                    CMS.debug(
+                            method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
+                    if (crm.hasPop()) {
+                        CMS.debug(method + " hasPop true");
+                        verifyPOP(locale, crm);
+                    } else { // no signing POP, then do it the hard way
+                        CMS.debug(method + "hasPop false, need to challenge");
+                        req.setExtData("cmc_POPchallengeRequired", "true");
+                    }
+                }
+
+            } else { //!verifyAllow
+
+                if (crm.hasPop()) {
+                    CMS.debug(method + " hasPop true");
                     verifyPOP(locale, crm);
+                } else { // no signing POP, then do it the hard way
+                    CMS.debug(method + "hasPop false, need to challenge");
+                    req.setExtData("cmc_POPchallengeRequired", "true");
                 }
-            } else {
-                CMS.debug("EnrollProfile: verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
-                verifyPOP(locale, crm);
             }
 
             fillCertReqMsg(locale, crm, info, req);
         } else {
-            CMS.debug("EnrollProfile: fillTaggedRequest: unsupported type (not CRMF or PKCS10)");
+            CMS.debug(method + " unsupported type (not CRMF or PKCS10)");
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"));
         }
@@ -1648,25 +1930,30 @@ public abstract class EnrollProfile extends BasicProfile
         return profileID;
     }
 
+    /*
+     * verifyPOP - CRMF POP verification for signing keys
+     */
     public void verifyPOP(Locale locale, CertReqMsg certReqMsg)
             throws EProfileException {
-        CMS.debug("EnrollProfile ::in verifyPOP");
+        String method = "EnrollProfile: verifyPOP: ";
+        CMS.debug(method + "for signing keys");
 
         String auditMessage = null;
         String auditSubjectID = auditSubjectID();
 
         if (!certReqMsg.hasPop()) {
-            return;
+            CMS.debug(method + "missing pop.");
+            popFailed(locale, auditSubjectID, auditMessage);
         }
         ProofOfPossession pop = certReqMsg.getPop();
         ProofOfPossession.Type popType = pop.getType();
 
         if (popType != ProofOfPossession.SIGNATURE) {
-            return;
+            CMS.debug(method + "pop type is not ProofOfPossession.SIGNATURE.");
+            popFailed(locale, auditSubjectID, auditMessage);
         }
 
         try {
-            CryptoManager cm = CryptoManager.getInstance();
             CryptoToken verifyToken = null;
             String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME);
             if (CryptoUtil.isInternalToken(tokenName)) {
@@ -1685,9 +1972,18 @@ public abstract class EnrollProfile extends BasicProfile
                     ILogger.SUCCESS);
             audit(auditMessage);
         } catch (Exception e) {
+            CMS.debug(method + "Unable to verify POP: " + e);
+            popFailed(locale, auditSubjectID, auditMessage, e);
+        }
+        CMS.debug(method + "ends.");
+    }
 
-            CMS.debug("EnrollProfile: Unable to verify POP: " + e);
-            CMS.debug(e);
+    private void popFailed(Locale locale, String auditSubjectID, String auditMessage)
+            throws EProfileException {
+        popFailed(locale, auditSubjectID, auditMessage, null);
+    }
+    private void popFailed(Locale locale, String auditSubjectID, String auditMessage, Exception e)
+            throws EProfileException {
 
             // store a message in the signed audit log file
             auditMessage = CMS.getLogMessage(
@@ -1697,8 +1993,12 @@ public abstract class EnrollProfile extends BasicProfile
 
             audit(auditMessage);
 
-            throw new EProfileException(CMS.getUserMessage(locale,
+            if (e != null) {
+                throw new EProfileException(CMS.getUserMessage(locale,
                         "CMS_POP_VERIFICATION_ERROR"), e);
-        }
+            } else {
+                throw new EProfileException(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 f1320f4..f5bef2a 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
@@ -30,16 +30,6 @@ import java.util.Hashtable;
 
 import javax.servlet.http.HttpServletResponse;
 
-import netscape.security.x509.CRLExtensions;
-import netscape.security.x509.CRLReasonExtension;
-import netscape.security.x509.CertificateChain;
-import netscape.security.x509.InvalidityDateExtension;
-import netscape.security.x509.RevocationReason;
-import netscape.security.x509.RevokedCertImpl;
-import netscape.security.x509.X500Name;
-import netscape.security.x509.X509CertImpl;
-import netscape.security.x509.X509Key;
-
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.asn1.ANY;
 import org.mozilla.jss.asn1.ASN1Util;
@@ -58,15 +48,18 @@ 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.EncryptedPOP;
 import org.mozilla.jss.pkix.cmc.GetCert;
 import org.mozilla.jss.pkix.cmc.OtherInfo;
 import org.mozilla.jss.pkix.cmc.OtherMsg;
 import org.mozilla.jss.pkix.cmc.PendInfo;
 import org.mozilla.jss.pkix.cmc.ResponseBody;
 import org.mozilla.jss.pkix.cmc.TaggedAttribute;
+import org.mozilla.jss.pkix.cmc.TaggedRequest;
 import org.mozilla.jss.pkix.cmmf.RevRequest;
 import org.mozilla.jss.pkix.cms.ContentInfo;
 import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo;
+import org.mozilla.jss.pkix.cms.EnvelopedData;
 import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber;
 import org.mozilla.jss.pkix.cms.SignedData;
 import org.mozilla.jss.pkix.cms.SignerIdentifier;
@@ -89,6 +82,17 @@ import com.netscape.certsrv.request.IRequest;
 import com.netscape.certsrv.request.IRequestQueue;
 import com.netscape.certsrv.request.RequestId;
 import com.netscape.certsrv.request.RequestStatus;
+import com.netscape.cmsutil.crypto.CryptoUtil;
+
+import netscape.security.x509.CRLExtensions;
+import netscape.security.x509.CRLReasonExtension;
+import netscape.security.x509.CertificateChain;
+import netscape.security.x509.InvalidityDateExtension;
+import netscape.security.x509.RevocationReason;
+import netscape.security.x509.RevokedCertImpl;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertImpl;
+import netscape.security.x509.X509Key;
 
 /**
  * Utility CMCOutputTemplate
@@ -142,6 +146,10 @@ public class CMCOutputTemplate {
 
     public void createFullResponse(HttpServletResponse resp, IRequest[] reqs,
             String cert_request_type, int[] error_codes) {
+        String method = "CMCOutputTemplate: createFullResponse: ";
+        CMS.debug(method +
+                "begins with cert_request_type=" +
+                cert_request_type);
 
         SEQUENCE controlSeq = new SEQUENCE();
         SEQUENCE cmsSeq = new SEQUENCE();
@@ -176,22 +184,33 @@ public class CMCOutputTemplate {
                     OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
             controlSeq.addElement(tagattr);
         } else if (cert_request_type.equals("cmc")) {
+            CMS.debug(method + " processing cmc");
             pending_bpids = new SEQUENCE();
             success_bpids = new SEQUENCE();
             failed_bpids = new SEQUENCE();
+            EncryptedPOP encPop = null;
             if (reqs != null) {
                 for (int i = 0; i < reqs.length; i++) {
+                    CMS.debug(method + " error_codes[i]=" + error_codes[i]);
                     if (error_codes[i] == 0) {
                         success_bpids.addElement(new INTEGER(
                                 reqs[i].getExtDataInBigInteger("bodyPartId")));
                     } else if (error_codes[i] == 2) {
                         pending_bpids.addElement(new INTEGER(
                                 reqs[i].getExtDataInBigInteger("bodyPartId")));
+                        try {
+                            encPop = constructEncryptedPop(reqs[i]);
+                        } catch (Exception e) {
+                            CMS.debug(method + e);
+                            return;
+                        }
                     } else {
                         failed_bpids.addElement(new INTEGER(
                                 reqs[i].getExtDataInBigInteger("bodyPartId")));
                     }
                 }
+            } else {
+                CMS.debug(method + " reqs null.  why?");
             }
 
             TaggedAttribute tagattr = null;
@@ -221,8 +240,25 @@ public class CMCOutputTemplate {
             }
 
             if (pending_bpids.size() > 0) {
+                // handle encryptedPOP control first
+
+                if (encPop != null) {
+                    CMS.debug(method + "adding encPop");
+                    tagattr = new TaggedAttribute(
+                            new INTEGER(bpid++),
+                            OBJECT_IDENTIFIER.id_cmc_encryptedPOP,
+                            encPop);
+                    controlSeq.addElement(tagattr);
+                    CMS.debug(method + "encPop added");
+                }
+
+                String reqId = reqs[0].getRequestId().toString();
+                OtherInfo otherInfo = null;
+                PendInfo pendInfo = new PendInfo(reqId, new Date());
+                otherInfo = new OtherInfo(OtherInfo.PEND, null,
+                        pendInfo);
                 cmcStatusInfo = new CMCStatusInfo(CMCStatusInfo.PENDING,
-                        pending_bpids, (String) null, null);
+                        pending_bpids, (String) null, otherInfo);
                 tagattr = new TaggedAttribute(
                         new INTEGER(bpid++),
                         OBJECT_IDENTIFIER.id_cmc_cMCStatusInfo, cmcStatusInfo);
@@ -238,7 +274,7 @@ public class CMCOutputTemplate {
                 } catch (Exception e) {
                 }
                 if (confirmRequired) {
-                    CMS.debug("CMCOutputTemplate: confirmRequired in the request");
+                    CMS.debug(method + " confirmRequired in the request");
                     cmcStatusInfo =
                             new CMCStatusInfo(CMCStatusInfo.CONFIRM_REQUIRED,
                                     success_bpids, (String) null, null);
@@ -270,13 +306,14 @@ public class CMCOutputTemplate {
             // deal with controls
             Integer nums = (Integer) (context.get("numOfControls"));
             if (nums != null && nums.intValue() > 0) {
+                CMS.debug(method + " processing controls");
                 TaggedAttribute attr =
                         (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_getCert));
                 if (attr != null) {
                     try {
                         processGetCertControl(attr, certs);
                     } catch (EBaseException ee) {
-                        CMS.debug("CMCOutputTemplate: " + ee.toString());
+                        CMS.debug(method + ee.toString());
                         OtherInfo otherInfo1 = new OtherInfo(OtherInfo.FAIL,
                                 new INTEGER(OtherInfo.BAD_CERT_ID), null);
                         SEQUENCE bpids1 = new SEQUENCE();
@@ -342,6 +379,10 @@ public class CMCOutputTemplate {
 
             ResponseBody respBody = new ResponseBody(controlSeq,
                     cmsSeq, otherMsgSeq);
+            if (respBody != null)
+                CMS.debug(method + " after new ResponseBody, respBody not null");
+            else
+                CMS.debug(method + " after new ResponseBody, respBody null");
 
             ContentInfo contentInfo = getContentInfo(respBody, certs);
             ByteArrayOutputStream fos = new ByteArrayOutputStream();
@@ -354,18 +395,79 @@ public class CMCOutputTemplate {
             OutputStream os = resp.getOutputStream();
             os.write(contentBytes);
             os.flush();
+            CMS.debug(method + "ends");
         } catch (java.security.cert.CertificateEncodingException e) {
-            CMS.debug("CMCOutputTemplate exception: " + e.toString());
+            CMS.debug(method + e.toString());
         } catch (InvalidBERException e) {
-            CMS.debug("CMCOutputTemplate exception: " + e.toString());
+            CMS.debug(method + e.toString());
         } catch (IOException e) {
-            CMS.debug("CMCOutputTemplate exception: " + e.toString());
+            CMS.debug(method + e.toString());
         } catch (Exception e) {
-            CMS.debug("Exception: " + e.toString());
+            CMS.debug(method + e.toString());
+        }
+    }
+
+    /**
+     * constructEncryptedPop pulls cmc pop challenge fields out of the request
+     * and constructs an EncryptedPOP
+     * to be included in the response later
+     *
+     * @author cfu
+     */
+    public EncryptedPOP constructEncryptedPop(IRequest req)
+            throws EBaseException {
+        String method = "CMCOutputTemplate: constructEncryptedPop: ";
+        CMS.debug(method + "begins");
+        EncryptedPOP encPop = null;
+
+        boolean popChallengeRequired = req.getExtDataInBoolean("cmc_POPchallengeRequired", false);
+        if (!popChallengeRequired) {
+            CMS.debug(method + "popChallengeRequired false");
+            return null;
         }
+        CMS.debug(method + "popChallengeRequired true");
+        byte[] cmc_msg = req.getExtDataInByteArray(IEnrollProfile.CTX_CERT_REQUEST);
+        byte[] pop_encreyptedData = req.getExtDataInByteArray("pop_encreyptedData");
+        //don't need this for encryptedPOP, but need to check for existence anyway
+        byte[] pop_sysPubEncreyptedSession = req.getExtDataInByteArray("pop_sysPubEncreyptedSession");
+        byte[] pop_userPubEncreyptedSession = req.getExtDataInByteArray("pop_userPubEncreyptedSession");
+        if ((pop_encreyptedData != null) &&
+                (pop_sysPubEncreyptedSession != null) &&
+                (pop_userPubEncreyptedSession != null)) {
+            // generate encryptedPOP here
+            // algs are hard-coded for now
+
+            try {
+                EnvelopedData envData = CryptoUtil.createEnvelopedData(
+                        pop_encreyptedData,
+                        pop_userPubEncreyptedSession);
+                ContentInfo ci = new ContentInfo(envData);
+                CMS.debug(method + "now we can compose encryptedPOP");
+
+                TaggedRequest.Template tReqTemplate = new TaggedRequest.Template();
+                TaggedRequest tReq = (TaggedRequest) tReqTemplate.decode(
+                        new ByteArrayInputStream(cmc_msg));
+
+                encPop = new EncryptedPOP(
+                        tReq,
+                        ci,
+                        CryptoUtil.getDefaultEncAlg(),
+                        CryptoUtil.getDefaultHashAlg(),
+                        new OCTET_STRING(req.getExtDataInByteArray("pop_witness")));
+
+            } catch (Exception e) {
+                CMS.debug(method + " excepton:" + e);
+                throw new EBaseException(method + " excepton:" + e);
+            }
+
+        }
+
+        return encPop;
     }
 
     private ContentInfo getContentInfo(ResponseBody respBody, SET certs) {
+        String method = "CMCOutputTemplate: getContentInfo: ";
+        CMS.debug(method + "begins");
         try {
             ICertificateAuthority ca = null;
             // add CA cert chain
@@ -442,10 +544,10 @@ public class CMCOutputTemplate {
                     enContentInfo, certs, null, signInfos);
 
             ContentInfo contentInfo = new ContentInfo(signedData);
-            CMS.debug("CMCOutputTemplate::getContentInfo() - done");
+            CMS.debug(method + " - done");
             return contentInfo;
         } catch (Exception e) {
-            CMS.debug("CMCOutputTemplate: Failed to create CMCContentInfo. Exception: " + e.toString());
+            CMS.debug(method + " Failed to create CMCContentInfo. Exception: " + e.toString());
         }
         return null;
     }
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 6191031..c233e41 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
@@ -28,8 +28,6 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import netscape.security.x509.X509CertImpl;
-
 import org.mozilla.jss.asn1.ASN1Util;
 import org.mozilla.jss.asn1.INTEGER;
 import org.mozilla.jss.asn1.InvalidBERException;
@@ -58,6 +56,7 @@ import com.netscape.certsrv.profile.IProfileInput;
 import com.netscape.certsrv.profile.IProfileSubsystem;
 import com.netscape.certsrv.request.INotify;
 import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.request.RequestId;
 import com.netscape.certsrv.request.RequestStatus;
 import com.netscape.cms.servlet.common.AuthCredentials;
 import com.netscape.cms.servlet.common.CMCOutputTemplate;
@@ -65,6 +64,8 @@ import com.netscape.cms.servlet.common.CMSRequest;
 import com.netscape.cms.servlet.common.CMSTemplate;
 import com.netscape.cmsutil.util.Utils;
 
+import netscape.security.x509.X509CertImpl;
+
 /**
  * This servlet submits end-user request into the profile framework.
  *
@@ -478,12 +479,13 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
         TaggedAttribute attr =
                 (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_lraPOPWitness));
         if (attr != null) {
-            boolean verifyAllow = true;
+            boolean verifyAllow = false; //disable RA by default
             try {
                 verifyAllow = CMS.getConfigStore().getBoolean(
-                        "cmc.lraPopWitness.verify.allow", true);
+                        "cmc.lraPopWitness.verify.allow", false);
             } catch (EBaseException ee) {
             }
+            CMS.debug("ProfileSubmitCMCServlet: cmc.lraPopWitness.verify.allow is " + verifyAllow);
 
             if (!verifyAllow) {
                 LraPopWitness lraPop = null;
@@ -507,23 +509,34 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
             }
         }
 
-        // for CMC, requests may be zero. Then check if controls exist.
+        // For CMC, requests may be zero. Then check if controls exist.
+        // In case of decryptedPOP, request already exists, find it and
+        // put in provedReq.
+        IRequest provedReq = null;
         if (reqs == null) {
-            Integer nums = (Integer) (context.get("numOfControls"));
-            CMCOutputTemplate template = new CMCOutputTemplate();
-            // if there is only one control GetCert, then simple response
-            // must be returned.
-            if (nums != null && nums.intValue() == 1) {
-                TaggedAttribute attr1 = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_getCert));
-                if (attr1 != null) {
-                    template.createSimpleResponse(response, reqs);
+            // handling DecryptedPOP request here
+            Integer reqID = (Integer) context.get("decryptedPopReqId");
+            provedReq = profile.getRequestQueue().findRequest(new RequestId(reqID.toString()));
+            if (provedReq == null) {
+
+                Integer nums = (Integer) (context.get("numOfControls"));
+                CMCOutputTemplate template = new CMCOutputTemplate();
+                // if there is only one control GetCert, then simple response
+                // must be returned.
+                if (nums != null && nums.intValue() == 1) {
+                    TaggedAttribute attr1 = (TaggedAttribute) (context.get(OBJECT_IDENTIFIER.id_cmc_getCert));
+                    if (attr1 != null) {
+                        template.createSimpleResponse(response, reqs);
+                    } else
+                        template.createFullResponse(response, reqs,
+                                cert_request_type, null);
                 } else
                     template.createFullResponse(response, reqs,
                             cert_request_type, null);
-            } else
-                template.createFullResponse(response, reqs,
-                        cert_request_type, null);
-            return;
+                return;
+            } else {
+                CMS.debug("ProfileSubmitCMCServlet: provedReq not null");
+            }
         }
 
         String errorCode = null;
@@ -532,7 +545,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
         ///////////////////////////////////////////////
         // populate request
         ///////////////////////////////////////////////
-        for (int k = 0; k < reqs.length; k++) {
+        for (int k = 0; (provedReq == null) &&(k < reqs.length); k++) {
             // adding parameters to request
             setInputsIntoRequest(request, profile, reqs[k]);
 
@@ -626,7 +639,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                         OtherInfo.INTERNAL_CA_ERROR, s);
                 return;
             }
-        }
+        } //for
 
         String auditMessage = null;
         String auditSubjectID = auditSubjectID();
@@ -640,7 +653,8 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
             int error_codes[] = null;
             if (reqs != null && reqs.length > 0)
                 error_codes = new int[reqs.length];
-            for (int k = 0; k < reqs.length; k++) {
+
+            for (int k = 0; (provedReq == null) && (k < reqs.length); k++) {
                 try {
                     // reset the "auditRequesterID"
                     auditRequesterID = auditRequesterID(reqs[k]);
@@ -680,6 +694,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                     }
                 } catch (EDeferException e) {
                     // return defer message to the user
+                    CMS.debug("ProfileSubmitCMCServlet: set request to PENDING");
                     reqs[k].setRequestStatus(RequestStatus.PENDING);
                     // need to notify
                     INotify notify = profile.getRequestQueue().getPendingNotify();
@@ -752,6 +767,53 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                     error_codes[k] = 0;
             }
 
+            // handle provedReq
+            if (provedReq != null) {
+                error_codes = new int[1];
+                auditRequesterID = auditRequesterID(provedReq);
+                try {
+                    profile.execute(provedReq);
+                    reqs = new IRequest[1];
+                    reqs[0] = provedReq;
+                    reqs[0].setRequestStatus(RequestStatus.COMPLETE);
+                    profile.getRequestQueue().markAsServiced(provedReq);
+                    CMS.debug("ProfileSubmitCMCServlet: provedReq set to complete");
+
+                    // reset the "auditInfoCertValue"
+                    auditInfoCertValue = auditInfoCertValue(reqs[0]);
+
+                    if (auditInfoCertValue != null) {
+                        if (!(auditInfoCertValue.equals(
+                                ILogger.SIGNED_AUDIT_EMPTY_VALUE))) {
+                            // store a message in the signed audit log file
+                            auditMessage = CMS.getLogMessage(
+                                    LOGGING_SIGNED_AUDIT_CERT_REQUEST_PROCESSED,
+                                    auditSubjectID,
+                                    ILogger.SUCCESS,
+                                    auditRequesterID,
+                                    ILogger.SIGNED_AUDIT_ACCEPTANCE,
+                                    auditInfoCertValue);
+
+                            audit(auditMessage);
+                        }
+                    }
+                } catch (ERejectException e) {
+                    // return error to the user
+                    provedReq.setRequestStatus(RequestStatus.REJECTED);
+                    CMS.debug("ProfileSubmitCMCServlet: provedReq submit " + e.toString());
+                    errorCode = "3";
+                    errorReason = CMS.getUserMessage(locale,
+                            "CMS_PROFILE_REJECTED",
+                            e.toString());
+                } catch (Exception e) {
+                    // return error to the user
+                    CMS.debug("ProfileSubmitCMCServlet: provedReq submit " + e.toString());
+                    errorCode = "1";
+                    errorReason = CMS.getUserMessage(locale,
+                            "CMS_INTERNAL_ERROR");
+                }
+            }
+
             if (errorCode != null) {
                 // create the CMC full enrollment response
                 CMCOutputTemplate template = new CMCOutputTemplate();
diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
index e3a378e..716a3f2 100644
--- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
+++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
@@ -55,10 +55,13 @@ import org.mozilla.jss.asn1.ANY;
 import org.mozilla.jss.asn1.ASN1Util;
 import org.mozilla.jss.asn1.ASN1Value;
 import org.mozilla.jss.asn1.BIT_STRING;
+import org.mozilla.jss.asn1.INTEGER;
 import org.mozilla.jss.asn1.InvalidBERException;
+import org.mozilla.jss.asn1.NULL;
 import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
 import org.mozilla.jss.asn1.OCTET_STRING;
 import org.mozilla.jss.asn1.SEQUENCE;
+import org.mozilla.jss.asn1.SET;
 import org.mozilla.jss.crypto.Algorithm;
 import org.mozilla.jss.crypto.BadPaddingException;
 import org.mozilla.jss.crypto.Cipher;
@@ -89,7 +92,11 @@ import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.pkcs11.PK11ECPublicKey;
 import org.mozilla.jss.pkcs11.PK11PubKey;
 import org.mozilla.jss.pkcs12.PasswordConverter;
-import org.mozilla.jss.pkcs7.EncryptedContentInfo;
+import org.mozilla.jss.pkcs7.IssuerAndSerialNumber;
+import org.mozilla.jss.pkcs7.RecipientInfo;
+import org.mozilla.jss.pkix.cms.ContentInfo;
+import org.mozilla.jss.pkix.cms.EncryptedContentInfo;
+import org.mozilla.jss.pkix.cms.EnvelopedData;
 import org.mozilla.jss.pkix.crmf.CertReqMsg;
 import org.mozilla.jss.pkix.crmf.CertRequest;
 import org.mozilla.jss.pkix.crmf.CertTemplate;
@@ -2391,6 +2398,41 @@ public class CryptoUtil {
     }
 
     /**
+     * for CMC encryptedPOP
+     */
+    public static EnvelopedData createEnvelopedData(byte[] encContent, byte[] encSymKey)
+            throws Exception {
+        String method = "CryptoUtl: createEnvelopedData: ";
+        System.out.println(method + "begins");
+
+        EncryptedContentInfo encCInfo = new EncryptedContentInfo(
+                ContentInfo.DATA,
+                getDefaultEncAlg(),
+                new OCTET_STRING(encContent));
+
+        Name name = new Name();
+        name.addCommonName("unUsedIssuerName"); //unused; okay for cmc EncryptedPOP
+        RecipientInfo recipient = new RecipientInfo(
+                new INTEGER(0), //per rfc2315
+                new IssuerAndSerialNumber(name, new INTEGER(0)), //unUsed
+                new AlgorithmIdentifier(RSA_ENCRYPTION, new NULL()),
+                new OCTET_STRING(encSymKey));
+
+        SET recipients = new SET();
+        recipients.addElement(recipient);
+
+        EnvelopedData envData = new EnvelopedData(
+                new INTEGER(0),
+                recipients,
+                encCInfo);
+
+        return envData;
+    }
+
+    /* PKCS 1 - rsaEncryption */
+    public static OBJECT_IDENTIFIER RSA_ENCRYPTION = new OBJECT_IDENTIFIER(new long[] { 1, 2, 840, 113549, 1, 1, 1 });
+
+    /**
      * The following are convenience routines for quick preliminary
      * feature development or test programs that would just take
      * the defaults
@@ -2538,6 +2580,32 @@ public class CryptoUtil {
         }
         return oid;
     }
+
+    /**
+     * getNameFromHashAlgorithm returns the hashing algorithm name
+     * from input Algorithm
+     *
+     * @param ai the hashing algorithm AlgorithmIdentifier
+     * @return name of the hashing algorithm
+     *
+     */
+    public static String getNameFromHashAlgorithm(AlgorithmIdentifier ai)
+           throws NoSuchAlgorithmException {
+        OBJECT_IDENTIFIER oid = null;
+
+        System.out.println("CryptoUtil: getNameFromHashAlgorithm: " + ai.getOID().toString());
+        if (ai != null) {
+            if (ai.getOID().equals((DigestAlgorithm.SHA256).toOID())) {
+                return "SHA-256";
+            } else if (ai.getOID().equals((DigestAlgorithm.SHA384).toOID())) {
+                return "SHA-384";
+            } else if (ai.getOID().equals((DigestAlgorithm.SHA512).toOID())) {
+                return "SHA-512";
+            }
+        }
+        throw new NoSuchAlgorithmException();
+    }
+
 }
 
 // START ENABLE_ECC
-- 
2.7.4

