From 617c8fe192fbc40b8aec0909462fa68ef51d2404 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Fri, 28 Apr 2017 17:55:17 -0700
Subject: [PATCH] Ticket #2717 CMC user-signed enrollment request This patch
 provides implementation that allows user-signed CMC requests to be processed;
 The resulting certificate will bear the same subjectDN as that of the signing
 cert; The new uri to access is /ca/ee/ca/profileSubmitUserSignedCMCFull where
 the new profile is to be used: caFullCMCUserSignedCert.cfg which utilizes the
 new authentication plugin: CMCUserSignedAuth and new profile default plugin:
 CMCUserSignedSubjectNameDefault and new profile constraint plugin:
 CMCUserSignedSubjectNameConstraint

---
 base/ca/shared/conf/CS.cfg                         |    6 +-
 base/ca/shared/conf/registry.cfg                   |   10 +-
 .../shared/profiles/ca/caFullCMCUserSignedCert.cfg |   83 ++
 base/ca/shared/webapps/ca/WEB-INF/web.xml          |   28 +
 .../certsrv/authentication/IAuthManager.java       |    3 +
 .../com/netscape/certsrv/logging/AuditEvent.java   |    2 +
 .../src/com/netscape/cmstools/CRMFPopClient.java   |    2 +-
 .../cms/authentication/CMCUserSignedAuth.java      | 1140 ++++++++++++++++++++
 .../netscape/cms/profile/common/BasicProfile.java  |   28 +-
 .../netscape/cms/profile/common/EnrollProfile.java |  426 +++++---
 .../CMCUserSignedSubjectNameConstraint.java        |  141 +++
 .../def/CMCUserSignedSubjectNameDefault.java       |  159 +++
 .../netscape/cms/profile/def/EnrollDefault.java    |    5 +-
 .../cms/profile/input/CMCCertReqInput.java         |   21 +-
 .../netscape/cms/profile/input/CertReqInput.java   |   36 +-
 .../servlet/profile/ProfileSubmitCMCServlet.java   |   28 +-
 base/server/cmsbundle/src/LogMessages.properties   |    1 +
 base/server/cmsbundle/src/UserMessages.properties  |    2 +
 18 files changed, 1964 insertions(+), 157 deletions(-)
 create mode 100644 base/ca/shared/profiles/ca/caFullCMCUserSignedCert.cfg
 create mode 100644 base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
 create mode 100644 base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java
 create mode 100644 base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java

diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg
index 3923319..b29802c 100644
--- a/base/ca/shared/conf/CS.cfg
+++ b/base/ca/shared/conf/CS.cfg
@@ -168,6 +168,7 @@ auths.impl._001=## authentication manager implementations
 auths.impl._002=##
 auths.impl.AgentCertAuth.class=com.netscape.cms.authentication.AgentCertAuthentication
 auths.impl.CMCAuth.class=com.netscape.cms.authentication.CMCAuth
+auths.impl.CMCUserSignedAuth.class=com.netscape.cms.authentication.CMCUserSignedAuth
 auths.impl.SSLclientCertAuth.class=com.netscape.cms.authentication.SSLclientCertAuthentication
 auths.impl.UidPwdDirAuth.class=com.netscape.cms.authentication.UidPwdDirAuthentication
 auths.impl.UidPwdPinDirAuth.class=com.netscape.cms.authentication.UidPwdPinDirAuthentication
@@ -734,6 +735,7 @@ ca.publish.rule.instance.LdapXCertRule.publisher=LdapCrossCertPairPublisher
 ca.publish.rule.instance.LdapXCertRule.type=xcert
 cmc.cert.confirmRequired=false
 cmc.lraPopWitness.verify.allow=false
+cmc.popLinkWitnessRequired=false
 cmc.revokeCert.verify=true
 cmc.revokeCert.sharedSecret.class=com.netscape.cms.authentication.SharedSecret
 cmc.sharedSecret.class=com.netscape.cms.authentication.SharedSecret
@@ -905,11 +907,11 @@ log.instance.SignedAudit._001=## Signed Audit Logging
 log.instance.SignedAudit._002=##
 log.instance.SignedAudit._003=##
 log.instance.SignedAudit._004=## Available Audit events:
-log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH_FAILURE,ACCESS_SESSION_ESTABLISH_SUCCESS,ACCESS_SESSION_TERMINATED
+log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,CMC_USER_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH_FAILURE,ACCESS_SESSION_ESTABLISH_SUCCESS,ACCESS_SESSION_TERMINATED
 log.instance.SignedAudit._006=##
 log.instance.SignedAudit.bufferSize=512
 log.instance.SignedAudit.enable=true
-log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH_FAILURE,ACCESS_SESSION_ESTABLISH_SUCCESS,ACCESS_SESSION_TERMINATED
+log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ_SUCCESS,AUTHZ_FAIL,INTER_BOUNDARY,AUTH_FAIL,AUTH_SUCCESS,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,CMC_USER_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_ADD_CA_REQUEST,OCSP_ADD_CA_REQUEST_PROCESSED,OCSP_REMOVE_CA_REQUEST,OCSP_REMOVE_CA_REQUEST_PROCESSED_SUCCESS,OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH_FAILURE,ACCESS_SESSION_ESTABLISH_SUCCESS,ACCESS_SESSION_TERMINATED
 log.instance.SignedAudit.expirationTime=0
 log.instance.SignedAudit.fileName=[PKI_INSTANCE_PATH]/logs/[PKI_SUBSYSTEM_TYPE]/signedAudit/ca_audit
 log.instance.SignedAudit.flushInterval=5
diff --git a/base/ca/shared/conf/registry.cfg b/base/ca/shared/conf/registry.cfg
index 2855b7a..519d854 100644
--- a/base/ca/shared/conf/registry.cfg
+++ b/base/ca/shared/conf/registry.cfg
@@ -1,5 +1,5 @@
 types=profile,defaultPolicy,constraintPolicy,profileInput,profileOutput,profileUpdater
-constraintPolicy.ids=noConstraintImpl,subjectNameConstraintImpl,uniqueSubjectNameConstraintImpl,userSubjectNameConstraintImpl,validityConstraintImpl,keyUsageExtConstraintImpl,nsCertTypeExtConstraintImpl,extendedKeyUsageExtConstraintImpl,keyConstraintImpl,basicConstraintsExtConstraintImpl,extensionConstraintImpl,signingAlgConstraintImpl,uniqueKeyConstraintImpl,renewGracePeriodConstraintImpl,authzRealmConstraintImpl,externalProcessConstraintImpl
+constraintPolicy.ids=noConstraintImpl,subjectNameConstraintImpl,uniqueSubjectNameConstraintImpl,userSubjectNameConstraintImpl,cmcUserSignedSubjectNameConstraintImpl,validityConstraintImpl,keyUsageExtConstraintImpl,nsCertTypeExtConstraintImpl,extendedKeyUsageExtConstraintImpl,keyConstraintImpl,basicConstraintsExtConstraintImpl,extensionConstraintImpl,signingAlgConstraintImpl,uniqueKeyConstraintImpl,renewGracePeriodConstraintImpl,authzRealmConstraintImpl,externalProcessConstraintImpl
 constraintPolicy.signingAlgConstraintImpl.class=com.netscape.cms.profile.constraint.SigningAlgConstraint
 constraintPolicy.signingAlgConstraintImpl.desc=Signing Algorithm Constraint
 constraintPolicy.signingAlgConstraintImpl.name=Signing Algorithm Constraint
@@ -36,6 +36,9 @@ constraintPolicy.uniqueSubjectNameConstraintImpl.name=Unique Subject Name Constr
 constraintPolicy.userSubjectNameConstraintImpl.class=com.netscape.cms.profile.constraint.UserSubjectNameConstraint
 constraintPolicy.userSubjectNameConstraintImpl.desc=User Subject Name Constraint
 constraintPolicy.userSubjectNameConstraintImpl.name=User Subject Name Constraint
+constraintPolicy.cmcUserSignedSubjectNameConstraintImpl.class=com.netscape.cms.profile.constraint.CMCUserSignedSubjectNameConstraint
+constraintPolicy.cmcUserSignedSubjectNameConstraintImpl.desc=CMC User Subject Name Constraint
+constraintPolicy.cmcUserSignedSubjectNameConstraintImpl.name=CMC User Subject Name Constraint
 constraintPolicy.validityConstraintImpl.class=com.netscape.cms.profile.constraint.ValidityConstraint
 constraintPolicy.validityConstraintImpl.desc=Validity Constraint
 constraintPolicy.validityConstraintImpl.name=Validity Constraint
@@ -48,7 +51,7 @@ constraintPolicy.uniqueKeyConstraintImpl.name=Unique Public Key Constraint
 constraintPolicy.externalProcessConstraintImpl.class=com.netscape.cms.profile.constraint.ExternalProcessConstraint
 constraintPolicy.externalProcessConstraintImpl.desc=External Process Constraint
 constraintPolicy.externalProcessConstraintImpl.name=External Process Constraint
-defaultPolicy.ids=noDefaultImpl,genericExtDefaultImpl,autoAssignDefaultImpl,subjectNameDefaultImpl,validityDefaultImpl,randomizedValidityDefaultImpl,caValidityDefaultImpl,subjectKeyIdentifierExtDefaultImpl,authorityKeyIdentifierExtDefaultImpl,basicConstraintsExtDefaultImpl,keyUsageExtDefaultImpl,nsCertTypeExtDefaultImpl,extendedKeyUsageExtDefaultImpl,ocspNoCheckExtDefaultImpl,issuerAltNameExtDefaultImpl,subjectAltNameExtDefaultImpl,userSubjectNameDefaultImpl,signingAlgDefaultImpl,userKeyDefaultImpl,userValidityDefaultImpl,userExtensionDefaultImpl,userSigningAlgDefaultImpl,authTokenSubjectNameDefaultImpl,subjectInfoAccessExtDefaultImpl,authInfoAccessExtDefaultImpl,nscCommentExtDefaultImpl,freshestCRLExtDefaultImpl,crlDistributionPointsExtDefaultImpl,policyConstraintsExtDefaultImpl,policyMappingsExtDefaultImpl,nameConstraintsExtDefaultImpl,certificateVersionDefaultImpl,certificatePoliciesExtDefaultImpl,subjectDirAttributesExtDefaultImpl,privateKeyPeriodExtDefaultImpl,inhibitAnyPolicyExtDefaultImpl,imageDefaultImpl,nsTokenDeviceKeySubjectNameDefaultImpl,nsTokenUserKeySubjectNameDefaultImpl,authzRealmDefaultImpl,commonNameToSANDefaultImpl
+defaultPolicy.ids=noDefaultImpl,genericExtDefaultImpl,autoAssignDefaultImpl,subjectNameDefaultImpl,validityDefaultImpl,randomizedValidityDefaultImpl,caValidityDefaultImpl,subjectKeyIdentifierExtDefaultImpl,authorityKeyIdentifierExtDefaultImpl,basicConstraintsExtDefaultImpl,keyUsageExtDefaultImpl,nsCertTypeExtDefaultImpl,extendedKeyUsageExtDefaultImpl,ocspNoCheckExtDefaultImpl,issuerAltNameExtDefaultImpl,subjectAltNameExtDefaultImpl,userSubjectNameDefaultImpl,cmcUserSignedSubjectNameDefaultImpl,signingAlgDefaultImpl,userKeyDefaultImpl,userValidityDefaultImpl,userExtensionDefaultImpl,userSigningAlgDefaultImpl,authTokenSubjectNameDefaultImpl,subjectInfoAccessExtDefaultImpl,authInfoAccessExtDefaultImpl,nscCommentExtDefaultImpl,freshestCRLExtDefaultImpl,crlDistributionPointsExtDefaultImpl,policyConstraintsExtDefaultImpl,policyMappingsExtDefaultImpl,nameConstraintsExtDefaultImpl,certificateVersionDefaultImpl,certificatePoliciesExtDefaultImpl,subjectDirAttributesExtDefaultImpl,privateKeyPeriodExtDefaultImpl,inhibitAnyPolicyExtDefaultImpl,imageDefaultImpl,nsTokenDeviceKeySubjectNameDefaultImpl,nsTokenUserKeySubjectNameDefaultImpl,authzRealmDefaultImpl,commonNameToSANDefaultImpl
 defaultPolicy.autoAssignDefaultImpl.class=com.netscape.cms.profile.def.AutoAssignDefault
 defaultPolicy.autoAssignDefaultImpl.desc=Auto Request Assignment Default
 defaultPolicy.autoAssignDefaultImpl.name=Auto Request Assignment Default
@@ -67,6 +70,9 @@ defaultPolicy.authTokenSubjectNameDefaultImpl.name=Token Supplied Subject Name D
 defaultPolicy.userSubjectNameDefaultImpl.class=com.netscape.cms.profile.def.UserSubjectNameDefault
 defaultPolicy.userSubjectNameDefaultImpl.desc=User Supplied Subject Name Default
 defaultPolicy.userSubjectNameDefaultImpl.name=User Supplied Subject Name Default
+defaultPolicy.cmcUserSignedSubjectNameDefaultImpl.class=com.netscape.cms.profile.def.CMCUserSignedSubjectNameDefault
+defaultPolicy.cmcUserSignedSubjectNameDefaultImpl.desc=CMC User Signed Subject Name Default
+defaultPolicy.cmcUserSignedSubjectNameDefaultImpl.name=CMC User Signed Subject Name Default
 defaultPolicy.userKeyDefaultImpl.class=com.netscape.cms.profile.def.UserKeyDefault
 defaultPolicy.userKeyDefaultImpl.desc=User Supplied Key Default
 defaultPolicy.userKeyDefaultImpl.name=User Supplied Key Default
diff --git a/base/ca/shared/profiles/ca/caFullCMCUserSignedCert.cfg b/base/ca/shared/profiles/ca/caFullCMCUserSignedCert.cfg
new file mode 100644
index 0000000..229a3cd
--- /dev/null
+++ b/base/ca/shared/profiles/ca/caFullCMCUserSignedCert.cfg
@@ -0,0 +1,83 @@
+desc=This certificate profile is for enrolling user certificates by using the CMC certificate request with user CMC Signature authentication.
+enable=true
+enableBy=admin
+name=User-Signed CMC-Authenticated User Certificate Enrollment
+visible=false
+auth.instance_id=CMCUserSignedAuth
+input.list=i1,i2
+input.i1.class_id=cmcCertReqInputImpl
+input.i2.class_id=submitterInfoInputImpl
+output.list=o1
+output.o1.class_id=certOutputImpl
+policyset.list=cmcUserCertSet
+policyset.cmcUserCertSet.list=1,2,3,4,5,6,7,8
+policyset.cmcUserCertSet.1.constraint.class_id=cmcUserSignedSubjectNameConstraintImpl
+policyset.cmcUserCertSet.1.constraint.name=CMC User Signed Subject Name Constraint
+policyset.cmcUserCertSet.1.default.class_id=cmcUserSignedSubjectNameDefaultImpl
+policyset.cmcUserCertSet.1.default.name=User Signed Subject Name Default
+policyset.cmcUserCertSet.1.default.params.name=
+policyset.cmcUserCertSet.2.constraint.class_id=validityConstraintImpl
+policyset.cmcUserCertSet.2.constraint.name=Validity Constraint
+policyset.cmcUserCertSet.2.constraint.params.notAfterCheck=false
+policyset.cmcUserCertSet.2.constraint.params.notBeforeCheck=false
+policyset.cmcUserCertSet.2.constraint.params.range=365
+policyset.cmcUserCertSet.2.default.class_id=validityDefaultImpl
+policyset.cmcUserCertSet.2.default.name=Validity Default
+policyset.cmcUserCertSet.2.default.params.range=180
+policyset.cmcUserCertSet.2.default.params.startTime=0
+policyset.cmcUserCertSet.3.constraint.class_id=keyConstraintImpl
+policyset.cmcUserCertSet.3.constraint.name=Key Constraint
+policyset.cmcUserCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp521
+policyset.cmcUserCertSet.3.constraint.params.keyType=-
+policyset.cmcUserCertSet.3.default.class_id=userKeyDefaultImpl
+policyset.cmcUserCertSet.3.default.name=Key Default
+policyset.cmcUserCertSet.4.constraint.class_id=noConstraintImpl
+policyset.cmcUserCertSet.4.constraint.name=No Constraint
+policyset.cmcUserCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl
+policyset.cmcUserCertSet.4.default.name=Authority Key Identifier Default
+policyset.cmcUserCertSet.5.constraint.class_id=noConstraintImpl
+policyset.cmcUserCertSet.5.constraint.name=No Constraint
+policyset.cmcUserCertSet.5.default.class_id=authInfoAccessExtDefaultImpl
+policyset.cmcUserCertSet.5.default.name=AIA Extension Default
+policyset.cmcUserCertSet.5.default.params.authInfoAccessADEnable_0=true
+policyset.cmcUserCertSet.5.default.params.authInfoAccessADLocationType_0=URIName
+policyset.cmcUserCertSet.5.default.params.authInfoAccessADLocation_0=
+policyset.cmcUserCertSet.5.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1
+policyset.cmcUserCertSet.5.default.params.authInfoAccessCritical=false
+policyset.cmcUserCertSet.5.default.params.authInfoAccessNumADs=1
+policyset.cmcUserCertSet.6.constraint.class_id=keyUsageExtConstraintImpl
+policyset.cmcUserCertSet.6.constraint.name=Key Usage Extension Constraint
+policyset.cmcUserCertSet.6.constraint.params.keyUsageCritical=true
+policyset.cmcUserCertSet.6.constraint.params.keyUsageCrlSign=false
+policyset.cmcUserCertSet.6.constraint.params.keyUsageDataEncipherment=false
+policyset.cmcUserCertSet.6.constraint.params.keyUsageDecipherOnly=false
+policyset.cmcUserCertSet.6.constraint.params.keyUsageDigitalSignature=true
+policyset.cmcUserCertSet.6.constraint.params.keyUsageEncipherOnly=false
+policyset.cmcUserCertSet.6.constraint.params.keyUsageKeyAgreement=false
+policyset.cmcUserCertSet.6.constraint.params.keyUsageKeyCertSign=false
+policyset.cmcUserCertSet.6.constraint.params.keyUsageKeyEncipherment=true
+policyset.cmcUserCertSet.6.constraint.params.keyUsageNonRepudiation=true
+policyset.cmcUserCertSet.6.default.class_id=keyUsageExtDefaultImpl
+policyset.cmcUserCertSet.6.default.name=Key Usage Default
+policyset.cmcUserCertSet.6.default.params.keyUsageCritical=true
+policyset.cmcUserCertSet.6.default.params.keyUsageCrlSign=false
+policyset.cmcUserCertSet.6.default.params.keyUsageDataEncipherment=false
+policyset.cmcUserCertSet.6.default.params.keyUsageDecipherOnly=false
+policyset.cmcUserCertSet.6.default.params.keyUsageDigitalSignature=true
+policyset.cmcUserCertSet.6.default.params.keyUsageEncipherOnly=false
+policyset.cmcUserCertSet.6.default.params.keyUsageKeyAgreement=false
+policyset.cmcUserCertSet.6.default.params.keyUsageKeyCertSign=false
+policyset.cmcUserCertSet.6.default.params.keyUsageKeyEncipherment=true
+policyset.cmcUserCertSet.6.default.params.keyUsageNonRepudiation=true
+policyset.cmcUserCertSet.7.constraint.class_id=noConstraintImpl
+policyset.cmcUserCertSet.7.constraint.name=No Constraint
+policyset.cmcUserCertSet.7.default.class_id=extendedKeyUsageExtDefaultImpl
+policyset.cmcUserCertSet.7.default.name=Extended Key Usage Extension Default
+policyset.cmcUserCertSet.7.default.params.exKeyUsageCritical=false
+policyset.cmcUserCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.4
+policyset.cmcUserCertSet.8.constraint.class_id=signingAlgConstraintImpl
+policyset.cmcUserCertSet.8.constraint.name=No Constraint
+policyset.cmcUserCertSet.8.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC
+policyset.cmcUserCertSet.8.default.class_id=signingAlgDefaultImpl
+policyset.cmcUserCertSet.8.default.name=Signing Alg
+policyset.cmcUserCertSet.8.default.params.signingAlg=-
diff --git a/base/ca/shared/webapps/ca/WEB-INF/web.xml b/base/ca/shared/webapps/ca/WEB-INF/web.xml
index bf8aed4..dc61ab3 100644
--- a/base/ca/shared/webapps/ca/WEB-INF/web.xml
+++ b/base/ca/shared/webapps/ca/WEB-INF/web.xml
@@ -1553,6 +1553,29 @@
    </servlet>
 
    <servlet>
+      <servlet-name>  caProfileSubmitUserSignedCMCFull  </servlet-name>
+      <servlet-class> com.netscape.cms.servlet.profile.ProfileSubmitCMCServlet  </servlet-class>
+             <init-param><param-name>  GetClientCert  </param-name>
+                         <param-value> false       </param-value> </init-param>
+             <init-param><param-name>  cert_request_type  </param-name>
+                         <param-value> cmc         </param-value> </init-param>
+             <init-param><param-name>  profileId   </param-name>
+                         <param-value> caFullCMCUserSignedCert </param-value> </init-param>
+             <init-param><param-name>  AuthzMgr    </param-name>
+                         <param-value> BasicAclAuthz </param-value> </init-param>
+             <init-param><param-name>  authorityId  </param-name>
+                         <param-value> ca          </param-value> </init-param>
+             <init-param><param-name>  ID          </param-name>
+                         <param-value> caProfileSubmitUserSignedCMCFull </param-value> </init-param>
+             <init-param><param-name>  templatePath  </param-name>
+                         <param-value> /ee/ca/ProfileSubmit.template </param-value> </init-param>
+             <init-param><param-name>  resourceID  </param-name>
+                         <param-value> certServer.ee.profile </param-value> </init-param>
+             <init-param><param-name>  interface   </param-name>
+                         <param-value> ee          </param-value> </init-param>
+   </servlet>
+
+   <servlet>
       <servlet-name>  caProfileList  </servlet-name>
       <servlet-class> com.netscape.cms.servlet.profile.ProfileListServlet  </servlet-class>
              <init-param><param-name>  GetClientCert  </param-name>
@@ -2257,6 +2280,11 @@
    </servlet-mapping>
 
    <servlet-mapping>  
+      <servlet-name>  caProfileSubmitUserSignedCMCFull  </servlet-name>
+      <url-pattern>   /ee/ca/profileSubmitUserSignedCMCFull  </url-pattern>
+   </servlet-mapping>
+
+   <servlet-mapping>  
       <servlet-name>  caProfileList  </servlet-name>
       <url-pattern>   /ee/ca/profileList  </url-pattern>
    </servlet-mapping>
diff --git a/base/common/src/com/netscape/certsrv/authentication/IAuthManager.java b/base/common/src/com/netscape/certsrv/authentication/IAuthManager.java
index f9eddbc..21639e2 100644
--- a/base/common/src/com/netscape/certsrv/authentication/IAuthManager.java
+++ b/base/common/src/com/netscape/certsrv/authentication/IAuthManager.java
@@ -31,6 +31,9 @@ public interface IAuthManager {
     /* standard credential for client cert from ssl client auth */
     public static final String CRED_SSL_CLIENT_CERT = "sslClientCert";
 
+    /* standard credential for CMC request signing cert */
+    public static final String CRED_CMC_SIGNING_CERT = "cmcSigningCert";
+
     /**
      * Standard credential for client cert's serial number from revocation.
      */
diff --git a/base/common/src/com/netscape/certsrv/logging/AuditEvent.java b/base/common/src/com/netscape/certsrv/logging/AuditEvent.java
index 72c93f8..7a4aa9b 100644
--- a/base/common/src/com/netscape/certsrv/logging/AuditEvent.java
+++ b/base/common/src/com/netscape/certsrv/logging/AuditEvent.java
@@ -145,6 +145,8 @@ public class AuditEvent implements IBundleLogEvent {
             "LOGGING_SIGNED_AUDIT_OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE_3";
     public final static String CMC_SIGNED_REQUEST_SIG_VERIFY =
             "LOGGING_SIGNED_AUDIT_CMC_SIGNED_REQUEST_SIG_VERIFY_5";
+    public final static String CMC_USER_SIGNED_REQUEST_SIG_VERIFY =
+            "LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_5";
 
     public final static String COMPUTE_RANDOM_DATA_REQUEST =
             "LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_2";
diff --git a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java
index 0168503..d0e5c27 100644
--- a/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java
+++ b/base/java-tools/src/com/netscape/cmstools/CRMFPopClient.java
@@ -545,7 +545,7 @@ public class CRMFPopClient {
                 pop = client.createPop(algorithm, signature);
             }
 
-            if (verbose) System.out.println("Creating CRMF requrest");
+            if (verbose) System.out.println("Creating CRMF request");
             String request = client.createCRMFRequest(certRequest, pop);
 
             StringWriter sw = new StringWriter();
diff --git a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
new file mode 100644
index 0000000..8b24ac3
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
@@ -0,0 +1,1140 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+// package statement //
+///////////////////////
+
+package com.netscape.cms.authentication;
+
+///////////////////////
+// import statements //
+///////////////////////
+
+/* cert server imports */
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+
+import org.mozilla.jss.CryptoManager;
+import org.mozilla.jss.CryptoManager.NotInitializedException;
+import org.mozilla.jss.asn1.ASN1Util;
+import org.mozilla.jss.asn1.INTEGER;
+import org.mozilla.jss.asn1.InvalidBERException;
+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.CryptoToken;
+import org.mozilla.jss.crypto.DigestAlgorithm;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.pkcs10.CertificationRequest;
+import org.mozilla.jss.pkcs11.PK11ECPublicKey;
+import org.mozilla.jss.pkcs11.PK11PubKey;
+import org.mozilla.jss.pkix.cert.Certificate;
+import org.mozilla.jss.pkix.cert.CertificateInfo;
+import org.mozilla.jss.pkix.cmc.PKIData;
+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.cms.EncapsulatedContentInfo;
+import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber;
+import org.mozilla.jss.pkix.cms.SignedData;
+import org.mozilla.jss.pkix.cms.SignerIdentifier;
+import org.mozilla.jss.pkix.crmf.CertReqMsg;
+import org.mozilla.jss.pkix.crmf.CertRequest;
+import org.mozilla.jss.pkix.crmf.CertTemplate;
+import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier;
+import org.mozilla.jss.pkix.primitive.Name;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.AuthToken;
+import com.netscape.certsrv.authentication.EInvalidCredentials;
+import com.netscape.certsrv.authentication.EMissingCredential;
+import com.netscape.certsrv.authentication.IAuthCredentials;
+import com.netscape.certsrv.authentication.IAuthManager;
+import com.netscape.certsrv.authentication.IAuthToken;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.IExtendedPluginInfo;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.logging.AuditEvent;
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.profile.EProfileException;
+import com.netscape.certsrv.profile.IProfile;
+import com.netscape.certsrv.profile.IProfileAuthenticator;
+import com.netscape.certsrv.property.Descriptor;
+import com.netscape.certsrv.property.IDescriptor;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.cmsutil.crypto.CryptoUtil;
+import com.netscape.cmsutil.util.Utils;
+
+import netscape.security.pkcs.PKCS10;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertImpl;
+import netscape.security.x509.X509CertInfo;
+import netscape.security.x509.X509Key;
+
+//import com.netscape.cmscore.util.*;
+//////////////////////
+// class definition //
+//////////////////////
+
+/**
+ * User Signed CMC authentication plug-in
+ * note:
+ *  - this version differs from CMCAuth in that it allows non-agent users
+ *  to sign own cmc requests;  It is expected to be used with
+ *  CMCUserSignedSubjectNameDefault and CMCUserSignedSubjectNameConstraint
+ *  so that the resulting cert will bear the same subjectDN of that of the CMC
+ *  signing cert
+ *  - it originates from CMCAuth with modification for user-signed cmc
+ * @author cfu - user signed cmc authentication
+ * <P>
+ *
+ * @version $Revision$, $Date$
+ */
+public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
+        IProfileAuthenticator {
+
+    ////////////////////////
+    // default parameters //
+    ////////////////////////
+
+    /////////////////////////////
+    // IAuthManager parameters //
+    /////////////////////////////
+
+    /* authentication plug-in configuration store */
+    private IConfigStore mConfig;
+    private static final String HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----";
+    private static final String TRAILER = "-----END NEW CERTIFICATE REQUEST-----";
+    public static final String TOKEN_CERT_SERIAL = "certSerialToRevoke";
+    public static final String REASON_CODE = "reasonCode";
+    /* authentication plug-in name */
+    private String mImplName = null;
+
+    /* authentication plug-in instance name */
+    private String mName = null;
+
+    /* authentication plug-in fields */
+
+    /* Holds authentication plug-in fields accepted by this implementation.
+     * This list is passed to the configuration console so configuration
+     * for instances of this implementation can be configured through the
+     * console.
+     */
+    protected static String[] mConfigParams =
+            new String[] {};
+
+    /* authentication plug-in values */
+
+    /* authentication plug-in properties */
+
+    /* required credentials to authenticate. UID and CMC are strings. */
+    public static final String CRED_CMC = "cmcRequest";
+
+    protected static String[] mRequiredCreds = {};
+
+    ////////////////////////////////////
+    // IExtendedPluginInfo parameters //
+    ////////////////////////////////////
+
+    /* Vector of extendedPluginInfo strings */
+    protected static Vector<String> mExtendedPluginInfo = null;
+    //public static final String AGENT_AUTHMGR_ID = "agentAuthMgr";
+    //public static final String AGENT_PLUGIN_ID = "agentAuthPlugin";
+
+    /* actual help messages */
+    static {
+        mExtendedPluginInfo = new Vector<String>();
+
+        mExtendedPluginInfo
+                .add(IExtendedPluginInfo.HELP_TEXT +
+                    ";Authenticate the CMC request. The \"Authentication Instance ID\" must be named \"CMCUserSignedAuth\"");
+        mExtendedPluginInfo.add(IExtendedPluginInfo.HELP_TOKEN +
+                ";configuration-authentication");
+    }
+
+    ///////////////////////
+    // Logger parameters //
+    ///////////////////////
+
+    /* the system's logger */
+    private ILogger mLogger = CMS.getLogger();
+
+    /* signed audit parameters */
+    private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
+    private final static String SIGNED_AUDIT_ENROLLMENT_REQUEST_TYPE =
+            "enrollment";
+    private final static String SIGNED_AUDIT_REVOCATION_REQUEST_TYPE =
+            "revocation";
+
+    /////////////////////
+    // default methods //
+    /////////////////////
+
+    /**
+     * Default constructor, initialization must follow.
+     */
+    public CMCUserSignedAuth() {
+    }
+
+    //////////////////////////
+    // IAuthManager methods //
+    //////////////////////////
+
+    /**
+     * Initializes the CMCUserSignedAuth authentication plug-in.
+     * <p>
+     *
+     * @param name The name for this authentication plug-in instance.
+     * @param implName The name of the authentication plug-in.
+     * @param config - The configuration store for this instance.
+     * @exception EBaseException If an error occurs during initialization.
+     */
+    public void init(String name, String implName, IConfigStore config)
+            throws EBaseException {
+        mName = name;
+        mImplName = implName;
+        mConfig = config;
+
+        log(ILogger.LL_INFO, "Initialization complete!");
+    }
+
+    /**
+     * Authenticates user by their CMC;
+     * resulting AuthToken sets a TOKEN_SUBJECT for the subject name.
+     * <P>
+     *
+     * <ul>
+     * <li>signed.audit LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY used when CMC (user-pre-signed) cert
+     * requests or revocation requests are submitted and signature is verified
+     * </ul>
+     *
+     * @param authCred Authentication credentials, CRED_UID and CRED_CMC.
+     * @return an AuthToken
+     * @exception com.netscape.certsrv.authentication.EMissingCredential
+     *                If a required authentication credential is missing.
+     * @exception com.netscape.certsrv.authentication.EInvalidCredentials
+     *                If credentials failed authentication.
+     * @exception com.netscape.certsrv.base.EBaseException
+     *                If an internal error occurred.
+     * @see com.netscape.certsrv.authentication.AuthToken
+     */
+    public IAuthToken authenticate(IAuthCredentials authCred) throws EMissingCredential, EInvalidCredentials,
+            EBaseException {
+        String method = "CMCUserSignedAuth: authenticate: ";
+        CMS.debug(method + "begins");
+
+        String auditMessage = null;
+        String auditSubjectID = auditSubjectID();
+        String auditReqType = ILogger.UNIDENTIFIED;
+        String auditCertSubject = ILogger.UNIDENTIFIED;
+        String auditSignerInfo = ILogger.UNIDENTIFIED;
+
+        // ensure that any low-level exceptions are reported
+        // to the signed audit log and stored as failures
+        try {
+            // get the CMC.
+
+            Object argblock = authCred.getArgBlock();
+            Object returnVal = null;
+            if (argblock == null) {
+                returnVal = authCred.get("cert_request");
+                if (returnVal == null)
+                    returnVal = authCred.get(CRED_CMC);
+            } else {
+                returnVal = authCred.get("cert_request");
+                if (returnVal == null)
+                    returnVal = authCred.getArgBlock().get(CRED_CMC);
+            }
+            String cmc = (String) returnVal;
+            if (cmc == null) {
+                CMS.debug(method + " Authentication failed. Missing CMC.");
+
+                // store a message in the signed audit log file
+                auditMessage = CMS.getLogMessage(
+                        AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                        auditSubjectID,
+                        ILogger.FAILURE,
+                        auditReqType,
+                        auditCertSubject,
+                        auditSignerInfo);
+
+                audit(auditMessage);
+
+                throw new EMissingCredential(CMS.getUserMessage(
+                        "CMS_AUTHENTICATION_NULL_CREDENTIAL", CRED_CMC));
+            }
+
+            if (cmc.equals("")) {
+                log(ILogger.LL_FAILURE,
+                        "cmc : attempted login with empty CMC.");
+
+                // store a message in the signed audit log file
+                auditMessage = CMS.getLogMessage(
+                        AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                        auditSubjectID,
+                        ILogger.FAILURE,
+                        auditReqType,
+                        auditCertSubject,
+                        auditSignerInfo);
+
+                audit(auditMessage);
+
+                throw new EInvalidCredentials(CMS.getUserMessage(
+                        "CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+            }
+
+            // authenticate by checking CMC.
+
+            // everything OK.
+            // now formulate the certificate info.
+            // set the subject name at a minimum.
+            // set anything else like version, extensions, etc.
+            // if nothing except subject name is set the rest of
+            // cert info will be filled in by policies and CA defaults.
+
+            AuthToken authToken = new AuthToken(this);
+
+            try {
+                String asciiBASE64Blob;
+
+                int startIndex = cmc.indexOf(HEADER);
+                int endIndex = cmc.indexOf(TRAILER);
+                if (startIndex != -1 && endIndex != -1) {
+                    startIndex = startIndex + HEADER.length();
+                    asciiBASE64Blob = cmc.substring(startIndex, endIndex);
+                } else
+                    asciiBASE64Blob = cmc;
+
+                byte[] cmcBlob = CMS.AtoB(asciiBASE64Blob);
+                ByteArrayInputStream cmcBlobIn = new
+                                                 ByteArrayInputStream(cmcBlob);
+
+                org.mozilla.jss.pkix.cms.ContentInfo cmcReq =
+                        (org.mozilla.jss.pkix.cms.ContentInfo)
+                        org.mozilla.jss.pkix.cms.ContentInfo.getTemplate().decode(
+                                cmcBlobIn);
+
+                if (!cmcReq.getContentType().equals(
+                        org.mozilla.jss.pkix.cms.ContentInfo.SIGNED_DATA) ||
+                        !cmcReq.hasContent()) {
+                    // store a message in the signed audit log file
+                    auditMessage = CMS.getLogMessage(
+                            AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                            auditSubjectID,
+                            ILogger.FAILURE,
+                            auditReqType,
+                            auditCertSubject,
+                            auditSignerInfo);
+
+                    audit(auditMessage);
+
+                    // throw new ECMSGWException(CMSGWResources.NO_CMC_CONTENT);
+
+                    throw new EBaseException("NO_CMC_CONTENT");
+                }
+
+                SignedData cmcFullReq = (SignedData)
+                                        cmcReq.getInterpretedContent();
+
+                IConfigStore cmc_config = CMS.getConfigStore();
+                boolean checkSignerInfo =
+                        cmc_config.getBoolean("cmc.signerInfo.verify", true);
+                String userid = "defUser";
+                String uid = "defUser";
+                if (checkSignerInfo) {
+                    IAuthToken userToken = verifySignerInfo(authToken, cmcFullReq);
+                    if (userToken == null) {
+                        CMS.debug(method + " authenticate() userToken null");
+                        throw new EBaseException(method + " verifySignerInfo failure");
+                    }
+                    userid = userToken.getInString("userid");
+                    uid = userToken.getInString("cn");
+                } else {
+                    CMS.debug(method + " authenticate() signerInfo verification bypassed");
+                }
+                // reset value of auditSignerInfo
+                if (uid != null) {
+                    auditSignerInfo = uid.trim();
+                }
+
+                EncapsulatedContentInfo ci = cmcFullReq.getContentInfo();
+
+                OBJECT_IDENTIFIER id = ci.getContentType();
+
+                if (!id.equals(OBJECT_IDENTIFIER.id_cct_PKIData) ||
+                        !ci.hasContent()) {
+                    // store a message in the signed audit log file
+                    auditMessage = CMS.getLogMessage(
+                            AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                            auditSubjectID,
+                            ILogger.FAILURE,
+                            auditReqType,
+                            auditCertSubject,
+                            auditSignerInfo);
+
+                    audit(auditMessage);
+
+                    //  throw new ECMSGWException(
+                    // CMSGWResources.NO_PKIDATA);
+
+                    throw new EBaseException("NO_PKIDATA");
+                }
+
+                OCTET_STRING content = ci.getContent();
+
+                ByteArrayInputStream s = new
+                        ByteArrayInputStream(content.toByteArray());
+                PKIData pkiData = (PKIData) (new PKIData.Template()).decode(s);
+
+                SEQUENCE reqSequence = pkiData.getReqSequence();
+
+                int numReqs = reqSequence.size();
+
+                if (numReqs == 0) {
+                    CMS.debug(method + "numReqs 0, assume revocation request");
+                    // revocation request
+
+                    // reset value of auditReqType
+                    auditReqType = SIGNED_AUDIT_REVOCATION_REQUEST_TYPE;
+
+                    SEQUENCE controlSequence = pkiData.getControlSequence();
+                    int controlSize = controlSequence.size();
+
+                    if (controlSize > 0) {
+                        for (int i = 0; i < controlSize; i++) {
+                            TaggedAttribute taggedAttribute =
+                                    (TaggedAttribute) controlSequence.elementAt(i);
+                            OBJECT_IDENTIFIER type = taggedAttribute.getType();
+
+                            if (type.equals(
+                                    OBJECT_IDENTIFIER.id_cmc_revokeRequest)) {
+/* TODO: user-signed revocation to be handled in next ticket
+                                // if( i ==1 ) {
+                                //     taggedAttribute.getType() ==
+                                //       OBJECT_IDENTIFIER.id_cmc_revokeRequest
+                                // }
+
+                                SET values = taggedAttribute.getValues();
+                                int numVals = values.size();
+                                BigInteger[] bigIntArray = null;
+
+                                bigIntArray = new BigInteger[numVals];
+                                for (int j = 0; j < numVals; j++) {
+                                    // serialNumber    INTEGER
+
+                                    // SEQUENCE RevRequest = (SEQUENCE)
+                                    //     values.elementAt(j);
+                                    byte[] encoded = ASN1Util.encode(
+                                            values.elementAt(j));
+                                    org.mozilla.jss.asn1.ASN1Template template = new
+                                            org.mozilla.jss.pkix.cmmf.RevRequest.Template();
+                                    org.mozilla.jss.pkix.cmmf.RevRequest revRequest =
+                                            (org.mozilla.jss.pkix.cmmf.RevRequest)
+                                            ASN1Util.decode(template, encoded);
+
+                                    // SEQUENCE RevRequest = (SEQUENCE)
+                                    //     ASN1Util.decode(
+                                    //         SEQUENCE.getTemplate(),
+                                    //         ASN1Util.encode(
+                                    //         values.elementAt(j)));
+
+                                    // SEQUENCE RevRequest =
+                                    //     values.elementAt(j);
+                                    // int revReqSize = RevRequest.size();
+                                    // if( revReqSize > 3 ) {
+                                    //     INTEGER serialNumber =
+                                    //         new INTEGER((long)0);
+                                    // }
+
+                                    INTEGER temp = revRequest.getSerialNumber();
+
+                                    bigIntArray[j] = temp;
+                                    authToken.set(TOKEN_CERT_SERIAL, bigIntArray);
+
+                                    long reasonCode = revRequest.getReason().getValue();
+                                    Integer IntObject = Integer.valueOf((int) reasonCode);
+                                    authToken.set(REASON_CODE, IntObject);
+
+                                    authToken.set("uid", uid);
+                                    authToken.set("userid", userid);
+                                }
+*/
+                            }
+                        }
+
+                    }
+                } else {
+                    CMS.debug(method + "numReqs not 0, assume enrollment request");
+                    // enrollment request
+
+                    // reset value of auditReqType
+                    auditReqType = SIGNED_AUDIT_ENROLLMENT_REQUEST_TYPE;
+
+                    X509CertInfo[] certInfoArray = new X509CertInfo[numReqs];
+                    String[] reqIdArray = new String[numReqs];
+
+                    for (int i = 0; i < numReqs; i++) {
+                        // decode message.
+                        TaggedRequest taggedRequest =
+                                (TaggedRequest) reqSequence.elementAt(i);
+
+                        TaggedRequest.Type type = taggedRequest.getType();
+
+                        if (type.equals(TaggedRequest.PKCS10)) {
+                            CMS.debug(method + " type is PKCS10");
+                            authToken.set("cert_request_type", "cmc-pkcs10");
+
+                            TaggedCertificationRequest tcr =
+                                    taggedRequest.getTcr();
+                            int p10Id = tcr.getBodyPartID().intValue();
+
+                            reqIdArray[i] = String.valueOf(p10Id);
+
+                            CertificationRequest p10 =
+                                    tcr.getCertificationRequest();
+
+                            // transfer to sun class
+                            ByteArrayOutputStream ostream =
+                                    new ByteArrayOutputStream();
+
+                            p10.encode(ostream);
+                            boolean sigver = true;
+                            boolean tokenSwitched = false;
+                            CryptoManager cm = null;
+                            CryptoToken signToken = null;
+                            CryptoToken savedToken = null;
+
+                            // for PKCS10, "sigver" would offer the POP
+                            sigver = CMS.getConfigStore().getBoolean("ca.requestVerify.enabled", true);
+                            try {
+                                cm = CryptoManager.getInstance();
+                                if (sigver == true) {
+                                    String tokenName =
+                                        CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME);
+                                    savedToken = cm.getThreadToken();
+                                    signToken = CryptoUtil.getCryptoToken(tokenName);
+                                    if (!savedToken.getName().equals(signToken.getName())) {
+                                        cm.setThreadToken(signToken);
+                                        tokenSwitched = true;
+                                    }
+                                }
+
+                                PKCS10 pkcs10 =
+                                        new PKCS10(ostream.toByteArray(), sigver);
+
+                                // xxx do we need to do anything else?
+                                X509CertInfo certInfo =
+                                        CMS.getDefaultX509CertInfo();
+
+                                // fillPKCS10(certInfo,pkcs10,authToken,null);
+
+                                // authToken.set(
+                                //     pkcs10.getSubjectPublicKeyInfo());
+
+                                X500Name tempName = pkcs10.getSubjectName();
+
+                                // reset value of auditCertSubject
+                                if (tempName != null) {
+                                    auditCertSubject =
+                                            tempName.toString().trim();
+                                    if (auditCertSubject.equals("")) {
+                                        auditCertSubject =
+                                                ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+                                    }
+                                    authToken.set(AuthToken.TOKEN_CERT_SUBJECT,
+                                              tempName.toString());
+                                }
+
+                                authToken.set("uid", uid);
+                                authToken.set("userid", userid);
+
+                                certInfoArray[i] = certInfo;
+                            } catch (Exception e) {
+                                // store a message in the signed audit log file
+                                auditMessage = CMS.getLogMessage(
+                                        AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                                        auditSubjectID,
+                                        ILogger.FAILURE,
+                                        auditReqType,
+                                        auditCertSubject,
+                                        auditSignerInfo);
+
+                                audit(auditMessage);
+
+                                //throw new ECMSGWException(
+                                //CMSGWResources.ERROR_PKCS101, e.toString());
+
+                                e.printStackTrace();
+                                throw new EBaseException(e.toString());
+                            } finally {
+                                if ((sigver == true) && (tokenSwitched == true)){
+                                    cm.setThreadToken(savedToken);
+                                }
+                             }
+                        } else if (type.equals(TaggedRequest.CRMF)) {
+
+                            CMS.debug(method + " type is CRMF");
+                            authToken.set("cert_request_type", "cmc-crmf");
+                            try {
+                                CertReqMsg crm =
+                                        taggedRequest.getCrm();
+                                CertRequest certReq = crm.getCertReq();
+                                INTEGER reqID = certReq.getCertReqId();
+                                reqIdArray[i] = reqID.toString();
+                                CertTemplate template = certReq.getCertTemplate();
+                                Name name = template.getSubject();
+
+                                // xxx do we need to do anything else?
+                                X509CertInfo certInfo =
+                                        CMS.getDefaultX509CertInfo();
+
+                                // reset value of auditCertSubject
+                                if (name != null) {
+                                    String ss = name.getRFC1485();
+
+                                    auditCertSubject = ss;
+                                    if (auditCertSubject.equals("")) {
+                                        auditCertSubject =
+                                                ILogger.SIGNED_AUDIT_EMPTY_VALUE;
+                                    }
+
+                                    authToken.set(AuthToken.TOKEN_CERT_SUBJECT, ss);
+                                    authToken.set("uid", uid);
+                                    authToken.set("userid", userid);
+                                }
+                                certInfoArray[i] = certInfo;
+                            } catch (Exception e) {
+                                // store a message in the signed audit log file
+                                auditMessage = CMS.getLogMessage(
+                                        AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                                        auditSubjectID,
+                                        ILogger.FAILURE,
+                                        auditReqType,
+                                        auditCertSubject,
+                                        auditSignerInfo);
+
+                                audit(auditMessage);
+
+                                //throw new ECMSGWException(
+                                //CMSGWResources.ERROR_PKCS101, e.toString());
+
+                                e.printStackTrace();
+                                throw new EBaseException(e.toString());
+                            }
+                        }
+
+                        // authToken.set(AgentAuthentication.CRED_CERT, new
+                        //     com.netscape.certsrv.usrgrp.Certificates(
+                        //     x509Certs));
+                    }
+                }
+            } catch (Exception e) {
+                CMS.debug(method + e);
+                // store a message in the signed audit log file
+                auditMessage = CMS.getLogMessage(
+                        AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                        auditSubjectID,
+                        ILogger.FAILURE,
+                        auditReqType,
+                        auditCertSubject,
+                        auditSignerInfo);
+
+                audit(auditMessage);
+
+                //Debug.printStackTrace(e);
+                throw new EInvalidCredentials(CMS.getUserMessage(
+                        "CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+            }
+
+            // store a message in the signed audit log file
+            auditMessage = CMS.getLogMessage(
+                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                    auditSubjectID,
+                    ILogger.SUCCESS,
+                    auditReqType,
+                    auditCertSubject,
+                    auditSignerInfo);
+
+            audit(auditMessage);
+
+            CMS.debug(method + "ends successfully; returning authToken");
+            return authToken;
+        } catch (EMissingCredential eAudit1) {
+            CMS.debug(method + eAudit1);
+            // store a message in the signed audit log file
+            auditMessage = CMS.getLogMessage(
+                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                    auditSubjectID,
+                    ILogger.FAILURE,
+                    auditReqType,
+                    auditCertSubject,
+                    auditSignerInfo);
+
+            audit(auditMessage);
+
+            // rethrow the specific exception to be handled later
+            throw eAudit1;
+        } catch (EInvalidCredentials eAudit2) {
+            CMS.debug(method + eAudit2);
+            // store a message in the signed audit log file
+            auditMessage = CMS.getLogMessage(
+                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                    auditSubjectID,
+                    ILogger.FAILURE,
+                    auditReqType,
+                    auditCertSubject,
+                    auditSignerInfo);
+
+            audit(auditMessage);
+
+            // rethrow the specific exception to be handled later
+            throw eAudit2;
+        } catch (EBaseException eAudit3) {
+            CMS.debug(method + eAudit3);
+            // store a message in the signed audit log file
+            auditMessage = CMS.getLogMessage(
+                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY,
+                    auditSubjectID,
+                    ILogger.FAILURE,
+                    auditReqType,
+                    auditCertSubject,
+                    auditSignerInfo);
+
+            audit(auditMessage);
+
+            // rethrow the specific exception to be handled later
+            throw eAudit3;
+        }
+    }
+
+    /**
+     * Returns a list of configuration parameter names.
+     * The list is passed to the configuration console so instances of
+     * this implementation can be configured through the console.
+     * <p>
+     *
+     * @return String array of configuration parameter names.
+     */
+    public String[] getConfigParams() {
+        return (mConfigParams);
+    }
+
+    /**
+     * gets the configuration substore used by this authentication
+     * plug-in
+     * <p>
+     *
+     * @return configuration store
+     */
+    public IConfigStore getConfigStore() {
+        return mConfig;
+    }
+
+    /**
+     * gets the plug-in name of this authentication plug-in.
+     */
+    public String getImplName() {
+        return mImplName;
+    }
+
+    /**
+     * gets the name of this authentication plug-in instance
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * get the list of required credentials.
+     * <p>
+     *
+     * @return list of required credentials as strings.
+     */
+    public String[] getRequiredCreds() {
+        return (mRequiredCreds);
+    }
+
+    /**
+     * prepares for shutdown.
+     */
+    public void shutdown() {
+    }
+
+    /////////////////////////////////
+    // IExtendedPluginInfo methods //
+    /////////////////////////////////
+
+    /**
+     * Activate the help system.
+     * <p>
+     *
+     * @return help messages
+     */
+    public String[] getExtendedPluginInfo() {
+        String method = "CMCUserSignedAuth: getExtendedPluginInfo: ";
+        CMS.debug(method + " begins");
+        String[] s = Utils.getStringArrayFromVector(mExtendedPluginInfo);
+
+        CMS.debug(method + " s.length = " + s.length);
+        for (int i = 0; i < s.length; i++) {
+            CMS.debug("" + i + " " + s[i]);
+        }
+        return s;
+    }
+
+    ////////////////////
+    // Logger methods //
+    ////////////////////
+
+    /**
+     * Logs a message for this class in the system log file.
+     * <p>
+     *
+     * @param level The log level.
+     * @param msg The message to log.
+     * @see com.netscape.certsrv.logging.ILogger
+     */
+    protected void log(int level, String msg) {
+        if (mLogger == null)
+            return;
+        mLogger.log(ILogger.EV_SYSTEM, null, ILogger.S_AUTHENTICATION,
+                level, "CMC Authentication: " + msg);
+    }
+
+    protected IAuthToken verifySignerInfo(AuthToken authToken, SignedData cmcFullReq) throws EBaseException {
+        String method = "CMCUserSignedAuth: verifySignerInfo: ";
+        CMS.debug(method + "begins");
+
+        EncapsulatedContentInfo ci = cmcFullReq.getContentInfo();
+        OBJECT_IDENTIFIER id = ci.getContentType();
+        OCTET_STRING content = ci.getContent();
+
+        boolean tokenSwitched = false;
+        CryptoToken signToken = null;
+        CryptoToken savedToken = null;
+        CryptoManager cm = null;
+        try {
+            cm = CryptoManager.getInstance();
+            ByteArrayInputStream s = new ByteArrayInputStream(content.toByteArray());
+            PKIData pkiData = (PKIData) (new PKIData.Template()).decode(s);
+
+            SET dais = cmcFullReq.getDigestAlgorithmIdentifiers();
+            int numDig = dais.size();
+            Hashtable<String, byte[]> digs = new Hashtable<String, byte[]>();
+
+            //if request key is used for signing, there MUST be only one signerInfo
+            //object in the signedData object.
+            for (int i = 0; i < numDig; i++) {
+                AlgorithmIdentifier dai =
+                        (AlgorithmIdentifier) dais.elementAt(i);
+                String name =
+                        DigestAlgorithm.fromOID(dai.getOID()).toString();
+
+                MessageDigest md =
+                        MessageDigest.getInstance(name);
+
+                byte[] digest = md.digest(content.toByteArray());
+
+                digs.put(name, digest);
+            }
+
+            SET sis = cmcFullReq.getSignerInfos();
+            int numSis = sis.size();
+
+            for (int i = 0; i < numSis; i++) {
+                org.mozilla.jss.pkix.cms.SignerInfo si = (org.mozilla.jss.pkix.cms.SignerInfo) sis.elementAt(i);
+
+                String name = si.getDigestAlgorithm().toString();
+                byte[] digest = digs.get(name);
+
+                if (digest == null) {
+                    MessageDigest md = MessageDigest.getInstance(name);
+                    ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+
+                    pkiData.encode(ostream);
+                    digest = md.digest(ostream.toByteArray());
+
+                }
+                // signed  by  previously certified signature key
+                SignerIdentifier sid = si.getSignerIdentifier();
+                // TODO: need to handle signing key being the matching key from
+                // the request
+                if (sid.getType().equals(SignerIdentifier.ISSUER_AND_SERIALNUMBER)) {
+                    IssuerAndSerialNumber issuerAndSerialNumber = sid.getIssuerAndSerialNumber();
+                    // find from the certs in the signedData
+                    java.security.cert.X509Certificate cert = null;
+
+                    if (cmcFullReq.hasCertificates()) {
+                        SET certs = cmcFullReq.getCertificates();
+                        int numCerts = certs.size();
+                        java.security.cert.X509Certificate[] x509Certs = new java.security.cert.X509Certificate[1];
+                        byte[] certByteArray = new byte[0];
+                        for (int j = 0; j < numCerts; j++) {
+                            Certificate certJss = (Certificate) certs.elementAt(j);
+                            CertificateInfo certI = certJss.getInfo();
+                            Name issuer = certI.getIssuer();
+
+                            byte[] issuerB = ASN1Util.encode(issuer);
+CMS.debug(method + "issuer = " + new String(issuerB));
+                            INTEGER sn = certI.getSerialNumber();
+                            // if this cert is the signer cert, not a cert in the chain
+                            if (new String(issuerB).equals(new String(
+                                    ASN1Util.encode(issuerAndSerialNumber.getIssuer())))
+                                    && sn.toString().equals(issuerAndSerialNumber.getSerialNumber().toString())) {
+                                ByteArrayOutputStream os = new
+                                        ByteArrayOutputStream();
+
+                                certJss.encode(os);
+                                certByteArray = os.toByteArray();
+
+                                X509CertImpl tempcert = new X509CertImpl(os.toByteArray());
+
+                                cert = tempcert;
+                                x509Certs[0] = cert;
+                                // xxx validate the cert length
+
+                            }
+                        }
+                        CMS.debug(method + "start checking signature");
+                        if (cert == null) {
+                            // find from certDB
+                            CMS.debug(method + "verifying signature");
+                            si.verify(digest, id);
+                        } else {
+                            CMS.debug(method + "found signing cert... verifying");
+                            PublicKey signKey = cert.getPublicKey();
+                            PrivateKey.Type keyType = null;
+                            String alg = signKey.getAlgorithm();
+
+                            PK11PubKey pubK = null;
+                            if (alg.equals("RSA")) {
+                                CMS.debug(method + "signing key alg=RSA");
+                                keyType = PrivateKey.RSA;
+                                pubK = PK11PubKey.fromRaw(keyType, ((X509Key) signKey).getKey());
+                            } else if (alg.equals("EC")) {
+                                CMS.debug(method + "signing key alg=EC");
+                                keyType = PrivateKey.EC;
+                                byte publicKeyData[] = ((X509Key) signKey).getEncoded();
+                                pubK = PK11ECPublicKey.fromSPKI(/*keyType,*/ publicKeyData);
+                            } else {
+                                CMS.debug(method + "unsupported signature algorithm: " + alg);
+                                throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+                            }
+
+                            String tokenName =
+                                CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME);
+                            // by default JSS will use internal crypto token
+                            if (!CryptoUtil.isInternalToken(tokenName)) {
+                                savedToken = cm.getThreadToken();
+                                signToken = CryptoUtil.getCryptoToken(tokenName);
+                                if(signToken != null) {
+                                    cm.setThreadToken(signToken);
+                                    tokenSwitched = true;
+                                    CMS.debug(method + "verifySignerInfo token switched:"+ tokenName);
+                                } else {
+                                    CMS.debug(method + "verifySignerInfo token not found:"+ tokenName+ ", trying internal");
+                                }
+                            }
+
+                            CMS.debug(method + "verifying signature with public key");
+                            si.verify(digest, id, pubK);
+                        }
+                        CMS.debug(method + "finished checking signature");
+                        // verify signer's certificate using the revocator
+                        if (!cm.isCertValid(certByteArray, true, CryptoManager.CertUsage.SSLClient)) {
+                            CMS.debug(method + "CMC signature failed to be verified");
+                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+                        } else {
+                            CMS.debug(method + "CMC signature verified; but signer not yet;");
+                        }
+                        // At this point, the signature has been verified;
+
+                        IAuthToken tempToken = new AuthToken(null);
+                        netscape.security.x509.X500Name tempPrincipal = (X500Name) x509Certs[0].getSubjectDN();
+                        String CN = tempPrincipal.getCommonName(); //tempToken.get("userid");
+                        CMS.debug(method + " Principal name = " + CN);
+
+                        BigInteger certSerial = x509Certs[0].getSerialNumber();
+                        CMS.debug(method + " verified cert serial=" + certSerial.toString());
+                        authToken.set(IAuthManager.CRED_CMC_SIGNING_CERT, certSerial.toString());
+                        tempToken.set("cn", CN);
+
+                        return tempToken;
+
+                    }
+
+                } else {
+                    CMS.debug(method + "unsupported SignerIdentifier type");
+                }
+            }
+        } catch (InvalidBERException e) {
+            CMS.debug(method + e.toString());
+        } catch (IOException e) {
+            CMS.debug(method + e.toString());
+        } catch (NotInitializedException e) {
+            CMS.debug(method + e.toString());
+        } catch (Exception e) {
+            CMS.debug(method + e.toString());
+            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
+        } finally {
+            if ((tokenSwitched == true) && (savedToken != null)){
+                cm.setThreadToken(savedToken);
+                CMS.debug(method + "verifySignerInfo token restored");
+            }
+        }
+        return null;
+
+    }
+
+    public String[] getExtendedPluginInfo(Locale locale) {
+        return null;
+    }
+
+    // Profile-related methods
+
+    public void init(IProfile profile, IConfigStore config)
+            throws EProfileException {
+    }
+
+    /**
+     * Retrieves the localizable name of this policy.
+     */
+    public String getName(Locale locale) {
+        return CMS.getUserMessage(locale, "CMS_AUTHENTICATION_CMS_SIGN_NAME");
+    }
+
+    /**
+     * Retrieves the localizable description of this policy.
+     */
+    public String getText(Locale locale) {
+        return CMS.getUserMessage(locale, "CMS_AUTHENTICATION_CMS_SIGN_TEXT");
+    }
+
+    /**
+     * Retrieves a list of names of the value parameter.
+     */
+    public Enumeration<String> getValueNames() {
+        Vector<String> v = new Vector<String>();
+        v.addElement("cert_request");
+        return v.elements();
+    }
+
+    public boolean isValueWriteable(String name) {
+        return false;
+    }
+
+    /**
+     * Retrieves the descriptor of the given value
+     * parameter by name.
+     */
+    public IDescriptor getValueDescriptor(Locale locale, String name) {
+        if (name.equals(CRED_CMC)) {
+            return new Descriptor(IDescriptor.STRING_LIST, null, null,
+                    "CMC request");
+        }
+        return null;
+    }
+
+    public void populate(IAuthToken token, IRequest request)
+            throws EProfileException {
+        request.setExtData(IProfileAuthenticator.AUTHENTICATED_NAME,
+                token.getInString(AuthToken.TOKEN_CERT_SUBJECT));
+    }
+
+    public boolean isSSLClientRequired() {
+        return false;
+    }
+
+    /**
+     * Signed Audit Log
+     *
+     * This method is called to store messages to the signed audit log.
+     * <P>
+     *
+     * @param msg signed audit log message
+     */
+    private void audit(String msg) {
+        // in this case, do NOT strip preceding/trailing whitespace
+        // from passed-in String parameters
+
+        if (mSignedAuditLogger == null) {
+            return;
+        }
+
+        mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+                null,
+                ILogger.S_SIGNED_AUDIT,
+                ILogger.LL_SECURITY,
+                msg);
+    }
+
+    protected void audit(AuditEvent event) {
+
+        String template = event.getMessage();
+        Object[] params = event.getParameters();
+
+        String message = CMS.getLogMessage(template, params);
+
+        audit(message);
+    }
+
+    /**
+     * Signed Audit Log Subject ID
+     *
+     * This method is called to obtain the "SubjectID" for
+     * a signed audit log message.
+     * <P>
+     *
+     * @return id string containing the signed audit log message SubjectID
+     */
+    private String auditSubjectID() {
+        // if no signed audit object exists, bail
+        if (mSignedAuditLogger == null) {
+            return null;
+        }
+
+        String subjectID = null;
+
+        // Initialize subjectID
+        SessionContext auditContext = SessionContext.getExistingContext();
+
+        if (auditContext != null) {
+            subjectID = (String)
+                    auditContext.get(SessionContext.USER_ID);
+
+            if (subjectID != null) {
+                subjectID = subjectID.trim();
+            } else {
+                subjectID = ILogger.NONROLEUSER;
+            }
+        } else {
+            subjectID = ILogger.UNIDENTIFIED;
+        }
+
+        return subjectID;
+    }
+}
diff --git a/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java
index e6fc045..e47c722 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java
@@ -783,6 +783,8 @@ public abstract class BasicProfile implements IProfile {
             boolean createConfig)
             throws EProfileException {
 
+        String method = "BasicProfile: createProfilePolicy: ";
+        CMS.debug(method + "begins");
         // String setId ex: policyset.set1
         // String id    Id of policy : examples: p1,p2,p3
         // String defaultClassId : id of the default plugin ex: validityDefaultImpl
@@ -911,19 +913,18 @@ public abstract class BasicProfile implements IProfile {
                 }
             }
         }
-
         String defaultRoot = id + "." + PROP_DEFAULT;
         String constraintRoot = id + "." + PROP_CONSTRAINT;
         IPluginInfo defInfo = mRegistry.getPluginInfo("defaultPolicy",
                 defaultClassId);
 
         if (defInfo == null) {
-            CMS.debug("BasicProfile: Cannot find " + defaultClassId);
+            CMS.debug(method + " Cannot find " + defaultClassId);
             throw new EProfileException("Cannot find " + defaultClassId);
         }
         String defaultClass = defInfo.getClassName();
 
-        CMS.debug("BasicProfile: loading default class " + defaultClass);
+        CMS.debug(method + " loading default class " + defaultClass);
         IPolicyDefault def = null;
 
         try {
@@ -931,7 +932,7 @@ public abstract class BasicProfile implements IProfile {
                     Class.forName(defaultClass).newInstance();
         } catch (Exception e) {
             // throw Exception
-            CMS.debug("BasicProfile: default policy " +
+            CMS.debug(method + " default policy " +
                     defaultClass + " " + e.toString());
         }
         if (def == null) {
@@ -941,24 +942,30 @@ public abstract class BasicProfile implements IProfile {
 
             defStore = policyStore.getSubStore(defaultRoot);
             def.init(this, defStore);
+            CMS.debug(method + " default class initialized.");
         }
 
         IPluginInfo conInfo = mRegistry.getPluginInfo("constraintPolicy",
                 constraintClassId);
+        if (conInfo == null) {
+            CMS.debug(method + " Cannot find " + constraintClassId);
+            throw new EProfileException("Cannot find " + constraintClassId);
+        }
         String constraintClass = conInfo.getClassName();
-        IPolicyConstraint constraint = null;
 
+        CMS.debug(method + " loading constraint class " + constraintClass);
+        IPolicyConstraint constraint = null;
         try {
             constraint = (IPolicyConstraint)
                     Class.forName(constraintClass).newInstance();
         } catch (Exception e) {
             // throw Exception
-            CMS.debug("BasicProfile: constraint policy " +
+            CMS.debug(method + " constraint policy " +
                     constraintClass + " " + e.toString());
         }
         ProfilePolicy policy = null;
         if (constraint == null) {
-            CMS.debug("BasicProfile: failed to create " + constraintClass);
+            CMS.debug(method + " failed to create " + constraintClass);
         } else {
             IConfigStore conStore = null;
 
@@ -966,9 +973,11 @@ public abstract class BasicProfile implements IProfile {
             constraint.init(this, conStore);
             policy = new ProfilePolicy(id, def, constraint);
             policies.addElement(policy);
+            CMS.debug(method + " constraint class initialized.");
         }
 
         if (createConfig) {
+            CMS.debug(method + " createConfig true; creating...");
             String list = null;
 
             try {
@@ -996,8 +1005,10 @@ public abstract class BasicProfile implements IProfile {
                 CMS.debug("BasicProfile: commiting config store " +
                         e.toString());
             }
+            CMS.debug(method + " config created.");
         }
 
+        CMS.debug(method + "ends");
         return policy;
     }
 
@@ -1091,9 +1102,10 @@ public abstract class BasicProfile implements IProfile {
      */
     public void populate(IRequest request)
             throws EProfileException {
+        String method = "BasicProfile: populate: ";
         String setId = getPolicySetId(request);
         Vector<IProfilePolicy> policies = getPolicies(setId);
-        CMS.debug("BasicProfile: populate() policy setid =" + setId);
+        CMS.debug(method + "policy setid =" + setId);
 
         for (int i = 0; i < policies.size(); i++) {
             IProfilePolicy policy = policies.elementAt(i);
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 1c44e2c..57f07d1 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
@@ -71,6 +71,7 @@ import org.mozilla.jss.pkix.primitive.Name;
 import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo;
 
 import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthManager;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.authentication.ISharedToken;
 import com.netscape.certsrv.authority.IAuthority;
@@ -110,6 +111,7 @@ import netscape.security.x509.CertificateX509Key;
 import netscape.security.x509.Extension;
 import netscape.security.x509.Extensions;
 import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertImpl;
 import netscape.security.x509.X509CertInfo;
 import netscape.security.x509.X509Key;
 
@@ -144,7 +146,8 @@ public abstract class EnrollProfile extends BasicProfile
      */
     public IRequest[] createRequests(IProfileContext ctx, Locale locale)
             throws EProfileException {
-        String method = "EnrollProfile: createRequests";
+
+        String method = "EnrollProfile: createRequests: ";
         CMS.debug(method + "begins");
 
         // determine how many requests should be created
@@ -171,13 +174,20 @@ public abstract class EnrollProfile extends BasicProfile
         }
         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;
-             */
+
+            // donePOI true means Proof-Of-Identity is already done.
+            // if the auth manager is the CMCUserSignedAuth, then
+            // the new cert will eventually have the same subject as the
+            // user signing cert
+            // if the auth manager is the CMCAuth (agent pre-approved),
+            // then no changes
+            boolean donePOI = false;
+            String signingUserSerial = ctx.get(IAuthManager.CRED_CMC_SIGNING_CERT);
+            if (signingUserSerial != null) {
+                donePOI = true;
+            }
             // catch for invalid request
-            cmc_msgs = parseCMC(locale, cert_request);
+            cmc_msgs = parseCMC(locale, cert_request, donePOI);
             if (cmc_msgs == null) {
                 CMS.debug(method + "parseCMC returns cmc_msgs null");
                 return null;
@@ -209,7 +219,7 @@ public abstract class EnrollProfile extends BasicProfile
             } 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");
+                    CMS.debug(method + "setting cmc TaggedRequest in request");
                     result[i].setExtData(
                             CTX_CERT_REQUEST,
                             ASN1Util.encode(cmc_msgs[i]));
@@ -221,9 +231,6 @@ public abstract class EnrollProfile extends BasicProfile
 
             // set requested CA
             result[i].setExtData(IRequest.AUTHORITY_ID, ctx.get(REQUEST_AUTHORITY_ID));
-
-            // set user data
-            result[i].setExtData(IRequest.USER_DATA, ctx.get(REQUEST_USER_DATA));
         }
         return result;
     }
@@ -300,7 +307,7 @@ public abstract class EnrollProfile extends BasicProfile
             req.setExtData(REQUEST_EXTENSIONS,
                     new CertificateExtensions());
 
-            CMS.debug("EnrollProfile: createRequest " +
+            CMS.debug("EnrollProfile: createEnrollmentRequest " +
                     req.getRequestId());
         } catch (EBaseException e) {
             // raise exception?
@@ -469,6 +476,7 @@ public abstract class EnrollProfile extends BasicProfile
 
         IRequestQueue queue = getRequestQueue();
         String msg = "";
+        CMS.debug(method + "begins");
 
         boolean popChallengeRequired =
                 request.getExtDataInBoolean("cmc_POPchallengeRequired", false);
@@ -485,7 +493,7 @@ public abstract class EnrollProfile extends BasicProfile
         }
 
         if (token == null){
-            CMS.debug(method + " auth token is null");
+            CMS.debug(method + " auth token is null; agent manual approval required;");
             CMS.debug(method + " validating request");
             validate(request);
             try {
@@ -500,6 +508,7 @@ public abstract class EnrollProfile extends BasicProfile
             // 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);
@@ -521,40 +530,38 @@ public abstract class EnrollProfile extends BasicProfile
         }
     }
 
-    /*
-     * parseCMC
-     * @throws EProfileException in case of error
-     *   note: returing "null" doesn't mean failure
+    /**
+     * getPKIDataFromCMCblob
+     *
+     * @param certReqBlob cmc b64 encoded blob
+     * @return PKIData
      */
-    public TaggedRequest[] parseCMC(Locale locale, String certreq)
+    public PKIData getPKIDataFromCMCblob(Locale locale, String certReqBlob)
             throws EProfileException {
 
-        String method = "EnrollProfile: parseCMC: ";
+        String method = "EnrollProfile: getPKIDataFromCMCblob: ";
         String msg = ""; // for capturing debug and throw info
 
         /* cert request must not be null */
-        if (certreq == null) {
-            msg = method + "certreq null";
+        if (certReqBlob == null) {
+            msg = method + "certReqBlob null";
             CMS.debug(msg);
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") +
                             msg);
         }
-        //CMS.debug(method + " Start parseCMC(): " + certreq);
+        //CMS.debug(method + " Start: " + certReqBlob);
         CMS.debug(method + "starts");
 
-        TaggedRequest msgs[] = null;
-
-        String creq = normalizeCertReq(certreq);
+        String creq = normalizeCertReq(certReqBlob);
         try {
             byte data[] = CMS.AtoB(creq);
-            ByteArrayInputStream cmcBlobIn =
-                    new ByteArrayInputStream(data);
+            ByteArrayInputStream cmcBlobIn = new ByteArrayInputStream(data);
 
-            org.mozilla.jss.pkix.cms.ContentInfo cmcReq = (org.mozilla.jss.pkix.cms.ContentInfo)
-                    org.mozilla.jss.pkix.cms.ContentInfo.getTemplate().decode(cmcBlobIn);
-            org.mozilla.jss.pkix.cms.SignedData cmcFullReq =
-                (org.mozilla.jss.pkix.cms.SignedData) cmcReq.getInterpretedContent();
+            org.mozilla.jss.pkix.cms.ContentInfo cmcReq = (org.mozilla.jss.pkix.cms.ContentInfo) org.mozilla.jss.pkix.cms.ContentInfo
+                    .getTemplate().decode(cmcBlobIn);
+            org.mozilla.jss.pkix.cms.SignedData cmcFullReq = (org.mozilla.jss.pkix.cms.SignedData) cmcReq
+                    .getInterpretedContent();
             org.mozilla.jss.pkix.cms.EncapsulatedContentInfo ci = cmcFullReq.getContentInfo();
             OCTET_STRING content = ci.getContent();
 
@@ -564,6 +571,104 @@ public abstract class EnrollProfile extends BasicProfile
             mCMCData = pkiData;
             //PKIData pkiData = (PKIData)
             //    (new PKIData.Template()).decode(cmcBlobIn);
+
+            return pkiData;
+        } catch (Exception e) {
+            CMS.debug(method + e);
+            throw new EProfileException(
+                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e);
+        }
+    }
+
+    public static CertificateSubjectName getCMCSigningCertSNfromCertSerial(
+            String certSerial) throws Exception {
+        X509CertImpl userCert = getCMCSigningCertFromCertSerial(certSerial);
+
+        if (userCert != null) {
+            return userCert.getSubjectObj();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * getCMCSigningCertFromCertSerial is to be used when authentication
+     * was done with CMCUserSignedAuth where the resulting
+     * authToken contains
+     * IAuthManager.CRED_CMC_SIGNING_CERT, serial number
+     * This method takes the serial number
+     * and finds the cert from the CA's certdb
+     */
+    public static X509CertImpl getCMCSigningCertFromCertSerial(
+            String certSerial) throws Exception {
+        String method = "EnrollProfile: getCMCSigningCertFromCertSerial: ";
+        String msg = "";
+
+        X509CertImpl userCert = null;
+
+        if (certSerial == null || certSerial.equals("")) {
+            msg = method + "certSerial empty";
+            CMS.debug(msg);
+            throw new Exception(msg);
+        }
+
+        // for CMCUserSignedAuth, the signing user is the subject of
+        // the new cert
+        ICertificateAuthority authority = (ICertificateAuthority) CMS.getSubsystem(CMS.SUBSYSTEM_CA);
+        try {
+            BigInteger serialNo = new BigInteger(certSerial);
+            userCert = authority.getCertificateRepository().getX509Certificate(serialNo);
+        } catch (NumberFormatException e) {
+            msg = method + e;
+            CMS.debug(msg);
+            throw new Exception(msg);
+        } catch (EBaseException e) {
+            msg = method + e + "; signing user cert not found: serial=" + certSerial;
+            CMS.debug(msg);
+            throw new Exception(msg);
+        }
+
+        if (userCert != null) {
+            msg = method + "signing user cert found; serial=" + certSerial;
+            CMS.debug(msg);
+        } else {
+            msg = method + "signing user cert not found: serial=" + certSerial;
+            CMS.debug(msg);
+            throw new Exception(msg);
+        }
+
+        return userCert;
+    }
+
+    /*
+     * parseCMC
+     * @throws EProfileException in case of error
+     *   note: returing "null" doesn't mean failure
+     */
+    public TaggedRequest[] parseCMC(Locale locale, String certreq)
+            throws EProfileException {
+        return parseCMC(locale, certreq, false);
+    }
+    public TaggedRequest[] parseCMC(Locale locale, String certreq, boolean donePOI)
+            throws EProfileException {
+
+        String method = "EnrollProfile: parseCMC: ";
+        String msg = ""; // for capturing debug and throw info
+        //CMS.debug(method + " Start parseCMC(): " + certreq);
+        CMS.debug(method + "starts");
+
+        /* cert request must not be null */
+        if (certreq == null) {
+            msg = method + "certreq null";
+            CMS.debug(msg);
+            throw new EProfileException(
+                    CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") +
+                            msg);
+        }
+
+        TaggedRequest msgs[] = null;
+        try {
+            PKIData pkiData = getPKIDataFromCMCblob(locale, certreq);
             SEQUENCE controlSeq = pkiData.getControlSequence();
             int numcontrols = controlSeq.size();
             SEQUENCE reqSeq = pkiData.getReqSequence();
@@ -571,6 +676,7 @@ public abstract class EnrollProfile extends BasicProfile
             UTF8String ident_s = null;
             SessionContext context = SessionContext.getContext();
             if (!context.containsKey("numOfControls")) {
+                CMS.debug(method + "numcontrols="+ numcontrols);
                 if (numcontrols > 0) {
                     context.put("numOfControls", Integer.valueOf(numcontrols));
                     TaggedAttribute[] attributes = new TaggedAttribute[numcontrols];
@@ -587,56 +693,45 @@ public abstract class EnrollProfile extends BasicProfile
                     boolean id_cmc_idPOPLinkRandom = false;
                     SET vals = null;
 
+                    /**
+                     * pre-process all controls --
+                     * the postponed processing is so that we can capture
+                     * the identification, if included
+                     */
+                    CMS.debug(method + "about to pre-process controls");
                     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_decryptedPOP)) {
-                            CMS.debug(method + " decryptedPOP found");
+                            CMS.debug(method + " id_cmc_decryptedPOP found");
                             id_cmc_decryptedPOP = true;
                             decPopVals = attributes[i].getValues();
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) {
+                            CMS.debug(method + " id_cmc_identification found");
                             id_cmc_identification = true;
                             ident = attributes[i].getValues();
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProofV2)) {
+                            CMS.debug(method + " id_cmc_identityProofV2 found");
                             id_cmc_identityProofV2 = true;
                             attr = attributes[i];
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProof)) {
+                            CMS.debug(method + " id_cmc_identityProof found");
                             id_cmc_identityProof = true;
                             attr = attributes[i];
                         } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_idPOPLinkRandom)) {
-                            CMS.debug(method + "id_cmc_idPOPLinkRandom true");
+                            CMS.debug(method + "id_cmc_idPOPLinkRandom found");
                             id_cmc_idPOPLinkRandom = true;
                             vals = attributes[i].getValues();
                         } else {
+                            CMS.debug(method + "unknown control found");
                             context.put(attributes[i].getType(), attributes[i]);
                         }
                     } //for
 
                     /**
                      * now do the actual control processing
-                     * (the postponed processing is so that we can capture
-                     * the identification, if included)
                      */
-
-                    if (id_cmc_decryptedPOP) {
-                        if (decPopVals != null) {
-
-                            DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(),
-                                    ASN1Util.encode(decPopVals.elementAt(0))));
-                            CMS.debug(method + "DecryptedPOP encoded");
-
-                            Integer reqId = verifyDecryptedPOP(locale, decPop);
-                            if (reqId != null) {
-                                context.put("decryptedPopReqId", reqId);
-                            }
-                        } else { //decPopVals == null
-                            msg = "id_cmc_decryptedPOP contains invalid DecryptedPOP";
-                            CMS.debug(method + msg);
-                            SEQUENCE bpids = getRequestBpids(reqSeq);
-                            context.put("decryptedPOP", bpids);
-                        }
-                        return null;
-                    }
+                    CMS.debug(method + "processing controls...");
 
                     if (id_cmc_identification) {
                         if (ident == null) {
@@ -666,8 +761,22 @@ public abstract class EnrollProfile extends BasicProfile
                         }
                     }
 
-                    // either V2 or not V2; can't be both
-                    if (id_cmc_identityProofV2 && (attr != null)) {
+                    // checking Proof Of Identity, if not pre-signed
+
+                    if (donePOI) {
+                        // for logging purposes
+                        if (id_cmc_identityProofV2) {
+                            CMS.debug(method
+                                    + "pre-signed CMC request, but id_cmc_identityProofV2 found...ignore; no further proof of identification check");
+                        } else if (id_cmc_identityProof) {
+                            CMS.debug(method
+                                    + "pre-signed CMC request, but id_cmc_identityProof found...ignore; no further proof of identification check");
+                        } else {
+                            CMS.debug(method + "pre-signed CMC request; no further proof of identification check");
+                        }
+                    } else if (id_cmc_identityProofV2 && (attr != null)) {
+                        // either V2 or not V2; can't be both
+                        CMS.debug(method + "not pre-signed CMC request; calling verifyIdentityProofV2;");
                         if (!id_cmc_identification) {
                             SEQUENCE bpids = getRequestBpids(reqSeq);
                             context.put("identification", bpids);
@@ -685,23 +794,57 @@ public abstract class EnrollProfile extends BasicProfile
                             SEQUENCE bpids = getRequestBpids(reqSeq);
                             context.put("identityProofV2", bpids);
 
-                            msg = " in verifyIdentityProofV2";
+                            msg = " after verifyIdentityProofV2";
                             CMS.debug(method + msg);
                             throw new EProfileException(CMS.getUserMessage(locale,
-                                    "CMS_POI_VERIFICATION_ERROR")+ msg);
+                                    "CMS_POI_VERIFICATION_ERROR") + msg);
+                        } else {
+                            CMS.debug(method + "passed verifyIdentityProofV2; Proof of Identity successful;");
                         }
                     } else if (id_cmc_identityProof && (attr != null)) {
+                        CMS.debug(method + "not pre-signed CMC request; calling verifyIdentityProof;");
                         boolean valid = verifyIdentityProof(attr,
                                 reqSeq);
                         if (!valid) {
                             SEQUENCE bpids = getRequestBpids(reqSeq);
                             context.put("identityProof", bpids);
 
-                            msg = " in verifyIdentityProof";
+                            msg = " after verifyIdentityProof";
                             CMS.debug(method + msg);
                             throw new EProfileException(CMS.getUserMessage(locale,
-                                    "CMS_POI_VERIFICATION_ERROR")+ msg);
+                                    "CMS_POI_VERIFICATION_ERROR") + msg);
+                        } else {
+                            CMS.debug(method + "passed verifyIdentityProof; Proof of Identity successful;");
                         }
+                    } else {
+                        msg = "not pre-signed CMC request; missing Proof of Identification control";
+                        CMS.debug(method + msg);
+                        throw new EProfileException(CMS.getUserMessage(locale,
+                                "CMS_POI_VERIFICATION_ERROR") + ":" + method + msg);
+                    }
+
+                    if (id_cmc_decryptedPOP) {
+                        if (decPopVals != null) {
+
+                            DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(),
+                                    ASN1Util.encode(decPopVals.elementAt(0))));
+                            CMS.debug(method + "DecryptedPOP encoded");
+
+                            Integer reqId = verifyDecryptedPOP(locale, decPop);
+                            if (reqId != null) {
+                                context.put("cmcDecryptedPopReqId", reqId);
+                            }
+                        } else { //decPopVals == null
+                            msg = "id_cmc_decryptedPOP contains invalid DecryptedPOP";
+                            CMS.debug(method + msg);
+                            SEQUENCE bpids = getRequestBpids(reqSeq);
+                            context.put("decryptedPOP", bpids);
+                        }
+
+                        // decryptedPOP is expected to return null;
+                        // POPLinkWitnessV2 would have to be checked in
+                        // round one, if required
+                        return null;
                     }
 
                     if (id_cmc_idPOPLinkRandom && vals != null) {
@@ -725,61 +868,65 @@ public abstract class EnrollProfile extends BasicProfile
                 }
             }
 
-            int nummsgs = reqSeq.size();
-            if (nummsgs > 0) {
+            /**
+             * in CS.cfg, cmc.popLinkWitnessRequired=true
+             * will enforce popLinkWitness (or V2);
+             */
+            boolean popLinkWitnessRequired = false;
+            try {
+                String configName = "cmc.popLinkWitnessRequired";
+                CMS.debug(method + "getting :" + configName);
+                popLinkWitnessRequired = CMS.getConfigStore().getBoolean(configName, false);
+            } catch (Exception e) {
+                // unlikely to get here
+                msg = method + " Failed to retrieve cmc.popLinkWitnessRequired";
+                CMS.debug(msg);
+                throw new EProfileException(method + msg);
+            }
 
+            int nummsgs = reqSeq.size();
+            if (!popLinkWitnessRequired) {
+                CMS.debug(method + "popLinkWitnessRequired false, skip check");
+            } else if (nummsgs > 0) {
+                CMS.debug(method + "cmc.popLinkWitnessRequired is true");
+                CMS.debug(method + "nummsgs =" + nummsgs);
                 msgs = new TaggedRequest[reqSeq.size()];
                 SEQUENCE bpids = new SEQUENCE();
 
-                /* TODO: add this in CS.cfg later: cmc.popLinkWitnessRequired=true
-                // enforce popLinkWitness (or V2)
-                boolean popLinkWitnessRequired = true;
-                try {
-                    String configName = "cmc.popLinkWitnessRequired";
-                    CMS.debug(method + "getting :" + configName);
-                    popLinkWitnessRequired = CMS.getConfigStore().getBoolean(configName, true);
-                    CMS.debug(method + "cmc.popLinkWitnessRequired is " + popLinkWitnessRequired);
-                } catch (Exception e) {
-                    // unlikely to get here
-                    msg = method + " Failed to retrieve cmc.popLinkWitnessRequired";
-                    CMS.debug(msg);
-                    throw new EProfileException(method + msg);
-                }
-*/
-
                 boolean valid = true;
                 for (int i = 0; i < nummsgs; i++) {
                     msgs[i] = (TaggedRequest) reqSeq.elementAt(i);
                     if (!context.containsKey("POPLinkWitnessV2") &&
                             !context.containsKey("POPLinkWitness")) {
-                        if (randomSeed != null) {
-                            // verifyPOPLinkWitness() will determine if this is
-                            // POPLinkWitnessV2 or POPLinkWitness
-                            // If failure, context is set in verifyPOPLinkWitness
-                            valid = verifyPOPLinkWitness(ident_s, randomSeed, msgs[i], bpids, context);
-                            if (valid == false) {
-                                if (context.containsKey("POPLinkWitnessV2"))
-                                    msg = " in POPLinkWitnessV2";
-                                else if (context.containsKey("POPLinkWitness"))
-                                    msg = " in POPLinkWitness";
-                                else
-                                    msg = " unspecified failure from verifyPOPLinkWitness";
-
-                                CMS.debug(method + msg);
-                                throw new EProfileException(CMS.getUserMessage(locale,
-                                        "MS_POP_LINK_WITNESS_VERIFICATION_ERROR")+ msg);
-                            }
-                        /* TODO: for next cmc ticket, eliminate the extra trip of parseCMC if possible, or figure a way out to bypass this on 2nd trip
-                        } else if (popLinkWitnessRequired == true) {
-                            //popLinkWitnessRequired == true, must have randomSeed
-                            CMS.debug(method + "popLinkWitness(V2) required; no randomSeed found");
+                        CMS.debug(method + "popLinkWitness(V2) required");
+                        if (randomSeed == null) {
+                            CMS.debug(method + "no randomSeed found");
                             context.put("POPLinkWitnessV2", bpids);
-                            return null;*/
-                        } //randomSeed != null
+                            return null;
+                        }
+
+                        // verifyPOPLinkWitness() will determine if this is
+                        // POPLinkWitnessV2 or POPLinkWitness
+                        // If failure, context is set in verifyPOPLinkWitness
+                        valid = verifyPOPLinkWitness(ident_s, randomSeed, msgs[i], bpids, context);
+                        if (valid == false) {
+                            if (context.containsKey("POPLinkWitnessV2"))
+                                msg = " in POPLinkWitnessV2";
+                            else if (context.containsKey("POPLinkWitness"))
+                                msg = " in POPLinkWitness";
+                            else
+                                msg = " unspecified failure from verifyPOPLinkWitness";
+
+                            CMS.debug(method + msg);
+                            throw new EProfileException(CMS.getUserMessage(locale,
+                                    "CMS_POP_LINK_WITNESS_VERIFICATION_ERROR") + msg);
+                        }
                     }
-                }
-            } else
+                } //for
+            } else {
+                CMS.debug(method + "nummsgs 0; returning...");
                 return null;
+            }
 
             CMS.debug(method + "ends");
             return msgs;
@@ -1398,6 +1545,9 @@ public abstract class EnrollProfile extends BasicProfile
     public void fillTaggedRequest(Locale locale, TaggedRequest tagreq, X509CertInfo info,
             IRequest req)
             throws EProfileException {
+        String auditMessage = null;
+        String auditSubjectID = auditSubjectID();
+
         String method = "EnrollProfile: fillTaggedRequest: ";
         CMS.debug(method + "begins");
         TaggedRequest.Type type = tagreq.getType();
@@ -1409,16 +1559,19 @@ public abstract class EnrollProfile extends BasicProfile
         }
 
         if (type.equals(TaggedRequest.PKCS10)) {
-            CMS.debug(method + " TaggedRequest type == pkcs10");
+            String methodPos = method + "PKCS10: ";
+            CMS.debug(methodPos + " TaggedRequest type == pkcs10");
             boolean sigver = true;
             boolean tokenSwitched = false;
             CryptoManager cm = null;
             CryptoToken signToken = null;
             CryptoToken savedToken = null;
             try {
+                // for PKCS10, "sigver" would provide the POP
                 sigver = CMS.getConfigStore().getBoolean("ca.requestVerify.enabled", true);
                 cm = CryptoManager.getInstance();
                 if (sigver == true) {
+                    CMS.debug(methodPos + "sigver true, POP is to be verified");
                     String tokenName =
                         CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME);
                     savedToken = cm.getThreadToken();
@@ -1427,6 +1580,12 @@ public abstract class EnrollProfile extends BasicProfile
                         cm.setThreadToken(signToken);
                         tokenSwitched = true;
                     }
+                } else {
+                    // normally, you would not get here, as you almost always
+                    // would want to verify the PKCS10 signature when it's
+                    // already there instead of taking a 2nd trip
+                    CMS.debug(methodPos + "sigver false, POP is not to be verified now, but instead will be challenged");
+                    req.setExtData("cmc_POPchallengeRequired", "true");
                 }
 
                 TaggedCertificationRequest tcr = tagreq.getTcr();
@@ -1440,13 +1599,17 @@ public abstract class EnrollProfile extends BasicProfile
                 fillPKCS10(locale, pkcs10, info, req);
             } catch (Exception e) {
                 CMS.debug(method + e);
+                // this will throw
+                popFailed(locale, auditSubjectID, auditMessage, e);
             }  finally {
                 if ((sigver == true) && (tokenSwitched == true)){
                     cm.setThreadToken(savedToken);
                 }
             }
+            CMS.debug(methodPos + "done");
         } else if (type.equals(TaggedRequest.CRMF)) {
-            CMS.debug(method + " TaggedRequest type == crmf");
+            String methodPos = method + "CRMF: ";
+            CMS.debug(methodPos + " TaggedRequest type == crmf");
             CertReqMsg crm = tagreq.getCrm();
             SessionContext context = SessionContext.getContext();
             Integer nums = (Integer) (context.get("numOfControls"));
@@ -1454,12 +1617,12 @@ public abstract class EnrollProfile extends BasicProfile
             boolean verifyAllow = false; //disable RA by default
             try {
                 String configName = "cmc.lraPopWitness.verify.allow";
-                CMS.debug(method + "getting :" + configName);
+                CMS.debug(methodPos + "getting :" + configName);
                 verifyAllow = CMS.getConfigStore().getBoolean(configName, false);
-                CMS.debug(method + "cmc.lraPopWitness.verify.allow is " + verifyAllow);
+                CMS.debug(methodPos + "cmc.lraPopWitness.verify.allow is " + verifyAllow);
             } catch (Exception e) {
                 // unlikely to get here
-                String msg = method + " Failed to retrieve cmc.lraPopWitness.verify.allow";
+                String msg = methodPos + " Failed to retrieve cmc.lraPopWitness.verify.allow";
                 CMS.debug(msg);
                 throw new EProfileException(method + msg);
             }
@@ -1471,23 +1634,23 @@ public abstract class EnrollProfile extends BasicProfile
                         parseLRAPopWitness(locale, crm, attr);
                     } else {
                         CMS.debug(
-                                method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
+                                methodPos + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
                         if (crm.hasPop()) {
-                            CMS.debug(method + " hasPop true");
+                            CMS.debug(methodPos + " hasPop true");
                             verifyPOP(locale, crm);
                         } else { // no signing POP, then do it the hard way
-                            CMS.debug(method + "hasPop false, need to challenge");
+                            CMS.debug(methodPos + "hasPop false, need to challenge");
                             req.setExtData("cmc_POPchallengeRequired", "true");
                         }
                     }
                 } else {
                     CMS.debug(
-                            method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
+                            methodPos + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request.");
                     if (crm.hasPop()) {
-                        CMS.debug(method + " hasPop true");
+                        CMS.debug(methodPos + " hasPop true");
                         verifyPOP(locale, crm);
                     } else { // no signing POP, then do it the hard way
-                        CMS.debug(method + "hasPop false, need to challenge");
+                        CMS.debug(methodPos + "hasPop false, need to challenge");
                         req.setExtData("cmc_POPchallengeRequired", "true");
                     }
                 }
@@ -1495,10 +1658,10 @@ public abstract class EnrollProfile extends BasicProfile
             } else { //!verifyAllow
 
                 if (crm.hasPop()) {
-                    CMS.debug(method + " hasPop true");
+                    CMS.debug(methodPos + " hasPop true");
                     verifyPOP(locale, crm);
                 } else { // no signing POP, then do it the hard way
-                    CMS.debug(method + "hasPop false, need to challenge");
+                    CMS.debug(methodPos + "hasPop false, need to challenge");
                     req.setExtData("cmc_POPchallengeRequired", "true");
                 }
             }
@@ -1835,6 +1998,8 @@ public abstract class EnrollProfile extends BasicProfile
 
     public void fillPKCS10(Locale locale, PKCS10 pkcs10, X509CertInfo info, IRequest req)
             throws EProfileException {
+        String method = "EnrollProfile: fillPKCS10: ";
+        CMS.debug(method + "begins");
         X509Key key = pkcs10.getSubjectPublicKeyInfo();
 
         try {
@@ -1869,7 +2034,7 @@ public abstract class EnrollProfile extends BasicProfile
                 PKCS10Attribute p10Attr = p10Attrs.getAttribute(CertificateExtensions.NAME);
                 if (p10Attr != null && p10Attr.getAttributeId().equals(
                         PKCS9Attribute.EXTENSION_REQUEST_OID)) {
-                    CMS.debug("Found PKCS10 extension");
+                    CMS.debug(method + "Found PKCS10 extension");
                     Extensions exts0 = (Extensions)
                             (p10Attr.getAttributeValue());
                     DerOutputStream extOut = new DerOutputStream();
@@ -1879,24 +2044,22 @@ public abstract class EnrollProfile extends BasicProfile
                     DerInputStream extIn = new DerInputStream(extB);
                     CertificateExtensions exts = new CertificateExtensions(extIn);
                     if (exts != null) {
-                        CMS.debug("Set extensions " + exts);
+                        CMS.debug(method + "Set extensions " + exts);
                         // info.set(X509CertInfo.EXTENSIONS, exts);
                         req.setExtData(REQUEST_EXTENSIONS, exts);
                     }
                 } else {
-                    CMS.debug("PKCS10 extension Not Found");
+                    CMS.debug(method + "PKCS10 extension Not Found");
                 }
             }
 
-            CMS.debug("Finish parsePKCS10 - " + pkcs10.getSubjectName());
+            CMS.debug(method + "Finish parsePKCS10 - " + pkcs10.getSubjectName());
         } catch (IOException e) {
-            CMS.debug("EnrollProfile: Unable to fill PKCS #10: " + e);
-            CMS.debug(e);
+            CMS.debug(method + "Unable to fill PKCS #10: " + e);
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e);
         } catch (CertificateException e) {
-            CMS.debug("EnrollProfile: Unable to fill PKCS #10: " + e);
-            CMS.debug(e);
+            CMS.debug(method + "Unable to fill PKCS #10: " + e);
             throw new EProfileException(
                     CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e);
         }
@@ -2074,8 +2237,11 @@ public abstract class EnrollProfile extends BasicProfile
 
     public void populate(IRequest request)
             throws EProfileException {
-        super.populate(request);
 
+        String method = "EnrollProfile: populate: ";
+        CMS.debug(method + "begins");
+
+        super.populate(request);
     }
 
     /**
@@ -2240,7 +2406,7 @@ public abstract class EnrollProfile extends BasicProfile
     public void verifyPOP(Locale locale, CertReqMsg certReqMsg)
             throws EProfileException {
         String method = "EnrollProfile: verifyPOP: ";
-        CMS.debug(method + "for signing keys");
+        CMS.debug(method + "for signing keys begins.");
 
         String auditMessage = null;
         String auditSubjectID = auditSubjectID();
@@ -2261,10 +2427,10 @@ public abstract class EnrollProfile extends BasicProfile
             CryptoToken verifyToken = null;
             String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME);
             if (CryptoUtil.isInternalToken(tokenName)) {
-                CMS.debug("POP verification using internal token");
+                CMS.debug(method + "POP verification using internal token");
                 certReqMsg.verify();
             } else {
-                CMS.debug("POP verification using token:" + tokenName);
+                CMS.debug(method + "POP verification using token:" + tokenName);
                 verifyToken = CryptoUtil.getCryptoToken(tokenName);
                 certReqMsg.verify(verifyToken);
             }
@@ -2279,7 +2445,7 @@ public abstract class EnrollProfile extends BasicProfile
             CMS.debug(method + "Unable to verify POP: " + e);
             popFailed(locale, auditSubjectID, auditMessage, e);
         }
-        CMS.debug(method + "ends.");
+        CMS.debug(method + "done.");
     }
 
     private void popFailed(Locale locale, String auditSubjectID, String auditMessage)
diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java
new file mode 100644
index 0000000..c71b670
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java
@@ -0,0 +1,141 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cms.profile.constraint;
+
+import java.util.Locale;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthManager;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.profile.EProfileException;
+import com.netscape.certsrv.profile.ERejectException;
+import com.netscape.certsrv.profile.IPolicyDefault;
+import com.netscape.certsrv.profile.IProfile;
+import com.netscape.certsrv.property.IDescriptor;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.cms.profile.common.EnrollProfile;
+import com.netscape.cms.profile.def.CMCUserSignedSubjectNameDefault;
+
+import netscape.security.x509.CertificateSubjectName;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertInfo;
+
+/**
+ * This class implements the user subject name constraint for user-signed cmc requests.
+ * It makes sure the signing cert's subjectDN and the rsulting cert match
+ *
+ * @author cfu
+ * @version $Revision$, $Date$
+ */
+public class CMCUserSignedSubjectNameConstraint extends EnrollConstraint {
+
+    public CMCUserSignedSubjectNameConstraint() {
+    }
+
+    public void init(IProfile profile, IConfigStore config)
+            throws EProfileException {
+        super.init(profile, config);
+    }
+
+    public IDescriptor getConfigDescriptor(Locale locale, String name) {
+        return null;
+    }
+
+    public String getDefaultConfig(String name) {
+        return null;
+    }
+
+    /**
+     * Validates the request. The request is not modified
+     * during the validation. User encoded subject name
+     * is copied into the certificate template.
+     */
+    public void validate(IRequest request, X509CertInfo info)
+            throws ERejectException {
+        String method = "CMCUserSignedSubjectNameConstraint: ";
+        String msg = "";
+
+        CMS.debug(method + "validate start");
+        CertificateSubjectName infoCertSN = null;
+            CertificateSubjectName authTokenCertSN = null;
+
+
+        try {
+            infoCertSN = (CertificateSubjectName) info.get(X509CertInfo.SUBJECT);
+            if (infoCertSN == null) {
+                msg = method + "infoCertSN null";
+                CMS.debug(msg);
+                throw new Exception(msg);
+            }
+            CMS.debug(method + "validate user subject ="+
+                      infoCertSN.toString());
+            String certSerial = request.getExtDataInString(IAuthManager.CRED_CMC_SIGNING_CERT);
+            if (certSerial == null) {
+                msg = method + "certSerial null";
+                CMS.debug(msg);
+                throw new Exception(msg);
+            }
+            authTokenCertSN =
+                          EnrollProfile.getCMCSigningCertSNfromCertSerial(certSerial);
+            if (authTokenCertSN == null) {
+                msg = method + "authTokenCertSN null";
+                CMS.debug(msg);
+                throw new Exception(msg);
+            }
+            X500Name infoCertName = (X500Name) infoCertSN.get(CertificateSubjectName.DN_NAME);
+            if (infoCertName == null) {
+                msg = method + "infoCertName null";
+                CMS.debug(msg);
+                throw new Exception(msg);
+            }
+            X500Name authTokenCertName = (X500Name) authTokenCertSN.get(CertificateSubjectName.DN_NAME);
+            if (authTokenCertName == null) {
+                msg = method + "authTokenCertName null";
+                CMS.debug(msg);
+                throw new Exception(msg);
+            }
+            if (infoCertName.equals(authTokenCertName)) {
+                CMS.debug(method + "names match");
+            } else {
+                msg = method + "names do not match";
+                CMS.debug(msg);
+                throw new Exception(msg);
+            }
+
+        } catch (Exception e) {
+            throw new ERejectException(
+                    CMS.getUserMessage(getLocale(request),
+                        "CMS_PROFILE_SUBJECT_NAME_NOT_MATCHED") + e);
+        }
+    }
+
+    public String getText(Locale locale) {
+        return CMS.getUserMessage(locale,
+                   "CMS_PROFILE_CONSTRAINT_CMC_USER_SIGNED_SUBJECT_NAME_TEXT");
+    }
+
+    public boolean isApplicable(IPolicyDefault def) {
+        String method = "CMCUserSignedSubjectNameConstraint: isApplicable: ";
+        if (def instanceof CMCUserSignedSubjectNameDefault) {
+            CMS.debug(method + "true");
+            return true;
+        }
+        CMS.debug(method + "false");
+        return false;
+    }
+}
diff --git a/base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java
new file mode 100644
index 0000000..a0816ea
--- /dev/null
+++ b/base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java
@@ -0,0 +1,159 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cms.profile.def;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthManager;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.profile.EProfileException;
+import com.netscape.certsrv.profile.IEnrollProfile;
+import com.netscape.certsrv.profile.IProfile;
+import com.netscape.certsrv.property.Descriptor;
+import com.netscape.certsrv.property.EPropertyException;
+import com.netscape.certsrv.property.IDescriptor;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.cms.profile.common.EnrollProfile;
+
+import netscape.security.x509.CertificateSubjectName;
+import netscape.security.x509.X500Name;
+import netscape.security.x509.X509CertInfo;
+
+/**
+ * This class implements an enrollment default policy
+ * that populates a CMC signing user's subject name
+ * into the certificate template.
+ *
+ * @author cfu
+ * @version $Revision$, $Date$
+ */
+public class CMCUserSignedSubjectNameDefault extends EnrollDefault {
+
+    public static final String VAL_NAME = "name";
+
+    public CMCUserSignedSubjectNameDefault() {
+        super();
+        addValueName(VAL_NAME);
+    }
+
+    public void init(IProfile profile, IConfigStore config)
+            throws EProfileException {
+        super.init(profile, config);
+    }
+
+    public IDescriptor getValueDescriptor(Locale locale, String name) {
+        if (name.equals(VAL_NAME)) {
+            return new Descriptor(IDescriptor.STRING, null, null,
+                    CMS.getUserMessage(locale, "CMS_PROFILE_SUBJECT_NAME"));
+        } else {
+            return null;
+        }
+    }
+
+    public void setValue(String name, Locale locale,
+            X509CertInfo info, String value)
+            throws EPropertyException {
+        if (name == null) {
+            throw new EPropertyException(CMS.getUserMessage(
+                        locale, "CMS_INVALID_PROPERTY", name));
+        }
+        if (name.equals(VAL_NAME)) {
+            X500Name x500name = null;
+
+            try {
+                x500name = new X500Name(value);
+            } catch (IOException e) {
+                CMS.debug(e.toString());
+                // failed to build x500 name
+            }
+            CMS.debug("SubjectNameDefault: setValue name=" + x500name);
+            try {
+                info.set(X509CertInfo.SUBJECT,
+                        new CertificateSubjectName(x500name));
+            } catch (Exception e) {
+                // failed to insert subject name
+                CMS.debug("CMCUserSignedSubjectNameDefault: setValue " + e.toString());
+                throw new EPropertyException(CMS.getUserMessage(
+                            locale, "CMS_INVALID_PROPERTY", name));
+            }
+        } else {
+            throw new EPropertyException(CMS.getUserMessage(
+                        locale, "CMS_INVALID_PROPERTY", name));
+        }
+    }
+
+    public String getValue(String name, Locale locale,
+            X509CertInfo info)
+            throws EPropertyException {
+        if (name == null) {
+            throw new EPropertyException(CMS.getUserMessage(
+                        locale, "CMS_INVALID_PROPERTY", name));
+        }
+        if (name.equals(VAL_NAME)) {
+            CertificateSubjectName sn = null;
+
+            try {
+                sn = (CertificateSubjectName)
+                        info.get(X509CertInfo.SUBJECT);
+                return sn.toString();
+            } catch (Exception e) {
+                // nothing
+            }
+            throw new EPropertyException(CMS.getUserMessage(
+                        locale, "CMS_INVALID_PROPERTY", name));
+        } else {
+            throw new EPropertyException(CMS.getUserMessage(
+                        locale, "CMS_INVALID_PROPERTY", name));
+        }
+    }
+
+    public String getText(Locale locale) {
+        return CMS.getUserMessage(locale, "CMS_PROFILE_DEF_CMC_USER_SIGNED_SUBJECT_NAME");
+    }
+
+    /**
+     * Populates the request with this policy default.
+     */
+    public void populate(IRequest request, X509CertInfo info)
+            throws EProfileException {
+        String method = "CMCUserSignedSubjectNameDefault: populate: ";
+        String msg = "";
+        CMS.debug(method + "begins");
+
+        String signingUserSerial = request.getExtDataInString(IAuthManager.CRED_CMC_SIGNING_CERT);
+        if (info == null) {
+            msg = method + "info null";
+            CMS.debug(msg);
+            throw new EProfileException(msg);
+        }
+
+        CertificateSubjectName certSN = null;
+        try {
+            certSN = EnrollProfile.getCMCSigningCertSNfromCertSerial(signingUserSerial);
+            info.set(X509CertInfo.SUBJECT, certSN);
+            CMS.debug(method + "subjectDN set in X509CertInfo");
+        } catch (Exception e) {
+            msg = method + "exception thrown:" + e;
+            throw new EProfileException(e.toString());
+        }
+        request.setExtData(IEnrollProfile.REQUEST_CERTINFO, info);
+        CMS.debug(method + "ends");
+    }
+}
diff --git a/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java
index 00d669e..1d5bfc4 100644
--- a/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java
+++ b/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java
@@ -214,17 +214,18 @@ public abstract class EnrollDefault implements IPolicyDefault, ICertInfoPolicyDe
      */
     public void populate(IRequest request)
             throws EProfileException {
+        String method = "EnrollDefault: populate: ";
         String name = getClass().getName();
 
         name = name.substring(name.lastIndexOf('.') + 1);
-        CMS.debug(name + ": populate start");
+        CMS.debug(method + name + ": start");
         X509CertInfo info =
                 request.getExtDataInCertInfo(IEnrollProfile.REQUEST_CERTINFO);
 
         populate(request, info);
 
         request.setExtData(IEnrollProfile.REQUEST_CERTINFO, info);
-        CMS.debug(name + ": populate end");
+        CMS.debug(method + name + ": end");
     }
 
     public void addValueName(String name) {
diff --git a/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java b/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java
index a62d6e9..0a9cae1 100644
--- a/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java
+++ b/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java
@@ -21,6 +21,8 @@ import java.util.Locale;
 
 import netscape.security.x509.X509CertInfo;
 
+import org.mozilla.jss.asn1.SEQUENCE;
+import org.mozilla.jss.pkix.cmc.PKIData;
 import org.mozilla.jss.pkix.cmc.TaggedRequest;
 
 import com.netscape.certsrv.apps.CMS;
@@ -85,19 +87,32 @@ public class CMCCertReqInput extends EnrollInput implements IProfileInput {
      */
     public void populate(IProfileContext ctx, IRequest request)
             throws EProfileException {
+        String method = "CMCCertReqInput: populate: ";
+        CMS.debug(method + "begins");
+
         String cert_request = ctx.get(VAL_CERT_REQUEST);
         X509CertInfo info =
                 request.getExtDataInCertInfo(EnrollProfile.REQUEST_CERTINFO);
 
         if (cert_request == null) {
-            CMS.debug("CMCCertReqInput: populate - invalid certificate request");
+            CMS.debug(method + "invalid certificate request");
             throw new EProfileException(CMS.getUserMessage(
                         getLocale(request), "CMS_PROFILE_NO_CERT_REQ"));
         }
-        TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request);
+        // cfu: getPKIDataFromCMCblob() is extracted from parseCMC
+        // so it's less confusing
+        //TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request);
+        PKIData pkiData = mEnrollProfile.getPKIDataFromCMCblob(getLocale(request), cert_request);
+        SEQUENCE reqSeq = pkiData.getReqSequence();
+        int nummsgs = reqSeq.size(); // for now we only handle one anyways
+        CMS.debug(method + "pkiData.getReqSequence() called; nummsgs =" + nummsgs);
+        TaggedRequest[] msgs = new TaggedRequest[reqSeq.size()];
+        for (int i = 0; i < nummsgs; i++) {
+            msgs[i] = (TaggedRequest) reqSeq.elementAt(i);
+        }
 
         if (msgs == null) {
-            CMS.debug("CMCCertReqInput: populate - parseCMC returns null TaggedRequest msgs");
+            CMS.debug(method + "TaggedRequest msgs null after getPKIDataFromCMCblob");
             return;
         }
         // This profile only handle the first request in CRMF
diff --git a/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java b/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java
index e67f5b5..fabd2aa 100644
--- a/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java
+++ b/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java
@@ -19,10 +19,8 @@ package com.netscape.cms.profile.input;
 
 import java.util.Locale;
 
-import netscape.security.pkcs.PKCS10;
-import netscape.security.util.DerInputStream;
-import netscape.security.x509.X509CertInfo;
-
+import org.mozilla.jss.asn1.SEQUENCE;
+import org.mozilla.jss.pkix.cmc.PKIData;
 import org.mozilla.jss.pkix.cmc.TaggedRequest;
 import org.mozilla.jss.pkix.crmf.CertReqMsg;
 
@@ -37,6 +35,10 @@ import com.netscape.certsrv.property.IDescriptor;
 import com.netscape.certsrv.request.IRequest;
 import com.netscape.cms.profile.common.EnrollProfile;
 
+import netscape.security.pkcs.PKCS10;
+import netscape.security.util.DerInputStream;
+import netscape.security.x509.X509CertInfo;
+
 /**
  * This class implements the certificate request input.
  * This input populates 2 main fields to the enrollment page:
@@ -89,13 +91,16 @@ public class CertReqInput extends EnrollInput implements IProfileInput {
      */
     public void populate(IProfileContext ctx, IRequest request)
             throws EProfileException {
+        String method = "CertReqInput: populate: ";
+        CMS.debug(method + "begins");
+
         String cert_request_type = ctx.get(VAL_CERT_REQUEST_TYPE);
         String cert_request = ctx.get(VAL_CERT_REQUEST);
         X509CertInfo info =
                 request.getExtDataInCertInfo(EnrollProfile.REQUEST_CERTINFO);
 
         if (cert_request_type == null) {
-            CMS.debug("CertReqInput: populate - invalid cert request type " +
+            CMS.debug(method + "invalid cert request type " +
                     "");
             throw new EProfileException(
                     CMS.getUserMessage(getLocale(request),
@@ -103,12 +108,14 @@ public class CertReqInput extends EnrollInput implements IProfileInput {
                             ""));
         }
         if (cert_request == null) {
-            CMS.debug("CertReqInput: populate - invalid certificate request");
+            CMS.debug(method + "invalid certificate request");
             throw new EProfileException(CMS.getUserMessage(
                         getLocale(request), "CMS_PROFILE_NO_CERT_REQ"));
         }
 
         if (cert_request_type.equals(EnrollProfile.REQ_TYPE_PKCS10)) {
+            CMS.debug(method + "cert_request_type= REQ_TYPE_PKCS10");
+
             PKCS10 pkcs10 = mEnrollProfile.parsePKCS10(getLocale(request), cert_request);
 
             if (pkcs10 == null) {
@@ -118,6 +125,7 @@ public class CertReqInput extends EnrollInput implements IProfileInput {
 
             mEnrollProfile.fillPKCS10(getLocale(request), pkcs10, info, request);
         } else if (cert_request_type.startsWith(EnrollProfile.REQ_TYPE_KEYGEN)) {
+            CMS.debug(method + "cert_request_type= REQ_TYPE_KEYGEN");
             DerInputStream keygen = mEnrollProfile.parseKeyGen(getLocale(request), cert_request);
 
             if (keygen == null) {
@@ -127,6 +135,7 @@ public class CertReqInput extends EnrollInput implements IProfileInput {
 
             mEnrollProfile.fillKeyGen(getLocale(request), keygen, info, request);
         } else if (cert_request_type.startsWith(EnrollProfile.REQ_TYPE_CRMF)) {
+            CMS.debug(method + "cert_request_type= REQ_TYPE_CRMF");
             CertReqMsg msgs[] = mEnrollProfile.parseCRMF(getLocale(request), cert_request);
 
             if (msgs == null) {
@@ -142,7 +151,18 @@ public class CertReqInput extends EnrollInput implements IProfileInput {
             mEnrollProfile.fillCertReqMsg(getLocale(request), msgs[seqNum.intValue()], info, request
                     );
         } else if (cert_request_type.startsWith(EnrollProfile.REQ_TYPE_CMC)) {
-            TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request);
+            CMS.debug(method + "cert_request_type= REQ_TYPE_CMC");
+            // cfu: getPKIDataFromCMCblob() is extracted from parseCMC
+            // so it's less confusing
+            //TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request);
+            PKIData pkiData = mEnrollProfile.getPKIDataFromCMCblob(getLocale(request), cert_request);
+            SEQUENCE reqSeq = pkiData.getReqSequence();
+            int nummsgs = reqSeq.size(); // for now we only handle one anyways
+            CMS.debug(method + "pkiData.getReqSequence() called; nummsgs =" + nummsgs);
+            TaggedRequest[] msgs = new TaggedRequest[reqSeq.size()];
+            for (int i = 0; i < nummsgs; i++) {
+                msgs[i] = (TaggedRequest) reqSeq.elementAt(i);
+            }
 
             if (msgs == null) {
                 throw new EProfileException(CMS.getUserMessage(
@@ -159,7 +179,7 @@ public class CertReqInput extends EnrollInput implements IProfileInput {
             mEnrollProfile.fillTaggedRequest(getLocale(request), msgs[seqNum.intValue()], info, request);
         } else {
             // error
-            CMS.debug("CertReqInput: populate - invalid cert request type " +
+            CMS.debug(method + "invalid cert request type " +
                     cert_request_type);
             throw new EProfileException(
                     CMS.getUserMessage(getLocale(request),
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 26ca2a4..1e128d0 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
@@ -39,6 +39,7 @@ import org.mozilla.jss.pkix.cmc.OtherInfo;
 import org.mozilla.jss.pkix.cmc.TaggedAttribute;
 
 import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.authentication.IAuthManager;
 import com.netscape.certsrv.authentication.IAuthToken;
 import com.netscape.certsrv.authorization.AuthzToken;
 import com.netscape.certsrv.base.EBaseException;
@@ -443,6 +444,18 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
         ///////////////////////////////////////////////
         // create request
         ///////////////////////////////////////////////
+        String tmpCertSerialS = ctx.get(IAuthManager.CRED_CMC_SIGNING_CERT);
+        if (tmpCertSerialS != null) {
+            // unlikely to happenm, but do this just in case
+            CMS.debug("ProfileSubmitCMCServlet: found existing CRED_CMC_SIGNING_CERT in ctx for CMCUserSignedAuth:" + tmpCertSerialS);
+            CMS.debug("ProfileSubmitCMCServlet: null it out");
+            ctx.set(IAuthManager.CRED_CMC_SIGNING_CERT, "");
+        }
+        String signingCertSerialS = (String) authToken.get(IAuthManager.CRED_CMC_SIGNING_CERT);
+        if (signingCertSerialS != null) {
+            CMS.debug("ProfileSubmitCMCServlet: setting CRED_CMC_SIGNING_CERT in ctx for CMCUserSignedAuth");
+            ctx.set(IAuthManager.CRED_CMC_SIGNING_CERT, signingCertSerialS);
+        }
         try {
             reqs = profile.createRequests(ctx, locale);
         } catch (EProfileException e) {
@@ -512,7 +525,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
         IRequest provedReq = null;
         if (reqs == null) {
             // handling DecryptedPOP request here
-            Integer reqID = (Integer) context.get("decryptedPopReqId");
+            Integer reqID = (Integer) context.get("cmcDecryptedPopReqId");
             provedReq = profile.getRequestQueue().findRequest(new RequestId(reqID.toString()));
             if (provedReq == null) {
 
@@ -568,6 +581,19 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
                         }
                     }
                 }
+
+                tmpCertSerialS = reqs[k].getExtDataInString(IAuthManager.CRED_CMC_SIGNING_CERT);
+                if (tmpCertSerialS != null) {
+                    // unlikely to happenm, but do this just in case
+                    CMS.debug("ProfileSubmitCMCServlet: found existing CRED_CMC_SIGNING_CERT in request for CMCUserSignedAuth:" + tmpCertSerialS);
+                    CMS.debug("ProfileSubmitCMCServlet: null it out");
+                    reqs[k].setExtData(IAuthManager.CRED_CMC_SIGNING_CERT, "");
+                }
+                // put CMCUserSignedAuth authToken in request
+                if (signingCertSerialS != null) {
+                    CMS.debug("ProfileSubmitCMCServlet: setting CRED_CMC_SIGNING_CERT in request for CMCUserSignedAuth");
+                    reqs[k].setExtData(IAuthManager.CRED_CMC_SIGNING_CERT, signingCertSerialS);
+                }
             }
 
             // put profile framework parameters into the request
diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties
index 7572db4..d3ac06a 100644
--- a/base/server/cmsbundle/src/LogMessages.properties
+++ b/base/server/cmsbundle/src/LogMessages.properties
@@ -2235,6 +2235,7 @@ LOGGING_SIGNED_AUDIT_OCSP_REMOVE_CA_REQUEST_PROCESSED_FAILURE_3=<type=OCSP_REMOV
 # SignerInfo must be a unique String representation for the signer
 #
 LOGGING_SIGNED_AUDIT_CMC_SIGNED_REQUEST_SIG_VERIFY_5=<type=CMC_SIGNED_REQUEST_SIG_VERIFY>:[AuditEvent=CMC_SIGNED_REQUEST_SIG_VERIFY][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}] agent pre-approved CMC request signature verification
+LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_5=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}] User signed CMC request signature verification
 
 # LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST 
 # - used for TPS to TKS to get random challenge data
diff --git a/base/server/cmsbundle/src/UserMessages.properties b/base/server/cmsbundle/src/UserMessages.properties
index bf96f90..ff56465 100644
--- a/base/server/cmsbundle/src/UserMessages.properties
+++ b/base/server/cmsbundle/src/UserMessages.properties
@@ -951,6 +951,7 @@ CMS_PROFILE_CONSTRAINT_SIGNING_ALG_TEXT=This constraint accepts only the Signing
 CMS_PROFILE_CONSTRAINT_SUBJECT_NAME_TEXT=This constraint accepts the subject name that matches {0}
 CMS_PROFILE_CONSTRAINT_UNIQUE_SUBJECT_NAME_TEXT=This constraint accepts unique subject name only
 CMS_PROFILE_CONSTRAINT_USER_SUBJECT_NAME_TEXT=This constraint accepts user subject name only
+CMS_PROFILE_CONSTRAINT_CMC_USER_SIGNED_SUBJECT_NAME_TEXT=This constraint accepts user subject name of the CMC request siging cert only
 CMS_PROFILE_CONSTRAINT_VALIDITY_TEXT=This constraint rejects the validity that is not between {0} days.
 CMS_PROFILE_CONSTRAINT_RENEWAL_GRACE_PERIOD_TEXT=This constraint rejects the renewal requests that are outside of the grace period {0}
 CMS_PROFILE_CONSTRAINT_VALIDITY_RENEWAL_TEXT=This constraint rejects the validity that is not between {0} days. If renewal, grace period is {1} days before and {2} days after the expiration date of the original certificate.
@@ -994,6 +995,7 @@ CMS_PROFILE_DEF_USER_KEY=This default populates a User-Supplied Certificate Key
 CMS_PROFILE_DEF_USER_SIGNING_ALGORITHM=This default populates a User-Supplied Certificate Signing Algorithm to the request.
 CMS_PROFILE_DEF_AUTHZ_REALM=This default populates an authorization realm.
 CMS_PROFILE_DEF_USER_SUBJECT_NAME=This default populates a User-Supplied Certificate Subject Name to the request.
+CMS_PROFILE_DEF_CMC_USER_SIGNED_SUBJECT_NAME=This default populates a User-Supplied Certificate Subject Name to the request.
 CMS_PROFILE_DEF_USER_VALIDITY=This default populates a User-Supplied Certificate Validity to the request.
 CMS_PROFILE_DEF_VALIDITY=This default populates a Certificate Validity to the request. The default values are Range={0} in days
 CMS_PROFILE_CERTIFICATE_POLICIES_ID=Certificate Policies ID
-- 
2.7.4

