Blame SOURCES/pki-core-CMC-check-HTTPS-client-authentication-cert.patch

3fd438
From 32cf3850935590f7f4cd457b824cc296b6af44b9 Mon Sep 17 00:00:00 2001
3fd438
From: Christina Fu <cfu@redhat.com>
3fd438
Date: Wed, 14 Jun 2017 14:57:10 -0700
3fd438
Subject: [PATCH 2/4] Ticket#2737 CMC: check HTTPS client authentication cert
3fd438
 against CMC signer
3fd438
3fd438
This patch adds enforcement in CMCUserSignedAuth to make sure SSL client authentication is performed and the authenticated cert matches that of the CMC signing cert.
3fd438
Some auditing adjustments are also done.
3fd438
3fd438
(cherry picked from commit 63c9582009b3858a6878863b9658d04c9aad45c1)
3fd438
---
3fd438
 base/ca/shared/conf/CS.cfg                         |   3 +-
3fd438
 .../com/netscape/certsrv/base/SessionContext.java  |   7 +
3fd438
 .../cms/authentication/CMCUserSignedAuth.java      | 220 ++++++++++++++-------
3fd438
 .../profile/constraint/UniqueKeyConstraint.java    |   8 +-
3fd438
 .../com/netscape/cms/servlet/base/CMSServlet.java  |  10 +-
3fd438
 .../servlet/profile/ProfileSubmitCMCServlet.java   |   7 +
3fd438
 base/server/cmsbundle/src/LogMessages.properties   |   4 +-
3fd438
 7 files changed, 175 insertions(+), 84 deletions(-)
3fd438
3fd438
diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg
3fd438
index d1bf7db..4da7429 100644
3fd438
--- a/base/ca/shared/conf/CS.cfg
3fd438
+++ b/base/ca/shared/conf/CS.cfg
3fd438
@@ -734,11 +734,10 @@ ca.publish.rule.instance.LdapXCertRule.pluginName=Rule
3fd438
 ca.publish.rule.instance.LdapXCertRule.predicate=
3fd438
 ca.publish.rule.instance.LdapXCertRule.publisher=LdapCrossCertPairPublisher
3fd438
 ca.publish.rule.instance.LdapXCertRule.type=xcert
3fd438
-cmc.cert.confirmRequired=false
3fd438
 cmc.popLinkWitnessRequired=false
3fd438
-cmc.revokeCert.verify=true
3fd438
 cmc.revokeCert.sharedSecret.class=com.netscape.cms.authentication.SharedSecret
3fd438
 cmc.sharedSecret.class=com.netscape.cms.authentication.SharedSecret
3fd438
+cmc.token=internal
3fd438
 cms.passwordlist=internaldb,replicationdb
3fd438
 cms.password.ignore.publishing.failure=true
3fd438
 cms.version=@APPLICATION_VERSION_MAJOR@.@APPLICATION_VERSION_MINOR@
3fd438
diff --git a/base/common/src/com/netscape/certsrv/base/SessionContext.java b/base/common/src/com/netscape/certsrv/base/SessionContext.java
3fd438
index 8bcb3c1..9323e6e 100644
3fd438
--- a/base/common/src/com/netscape/certsrv/base/SessionContext.java
3fd438
+++ b/base/common/src/com/netscape/certsrv/base/SessionContext.java
3fd438
@@ -56,6 +56,13 @@ public class SessionContext extends Hashtable<Object, Object> {
3fd438
      * Principal name object of the signed CMC request
3fd438
      */
3fd438
     public static final String CMC_SIGNER_PRINCIPAL = "cmcSignerPrincipal";
3fd438
+    public static final String CMC_SIGNER_INFO = "cmcSignerInfo";
3fd438
+    public static final String CMC_REQUEST_CERT_SUBJECT = "cmcRequestCertSubject";
3fd438
+
3fd438
+   /**
3fd438
+    * authenticated SSL client certificate
3fd438
+    */
3fd438
+    public static final String SSL_CLIENT_CERT = "sslClientCert";
3fd438
 
3fd438
     /**
3fd438
      * User object of the authenticated user in the current thread.
3fd438
diff --git a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
3fd438
index 2e4d6dc..6c3ee8f 100644
3fd438
--- a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
3fd438
+++ b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
3fd438
@@ -28,6 +28,7 @@ package com.netscape.cms.authentication;
3fd438
 import java.io.ByteArrayInputStream;
3fd438
 import java.io.ByteArrayOutputStream;
3fd438
 import java.io.IOException;
3fd438
+import java.security.cert.X509Certificate;
3fd438
 import java.math.BigInteger;
3fd438
 import java.security.MessageDigest;
3fd438
 import java.security.PublicKey;
3fd438
@@ -260,11 +261,27 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
         CMS.debug(method + "begins");
3fd438
 
3fd438
         String auditMessage = null;
3fd438
-        String auditSubjectID = auditSubjectID();
3fd438
+        String auditSubjectID = getAuditSubjectID();
3fd438
         String auditReqType = ILogger.UNIDENTIFIED;
3fd438
-        String auditCertSubject = ILogger.UNIDENTIFIED;
3fd438
+        String requestCertSubject = ILogger.UNIDENTIFIED;
3fd438
         String auditSignerInfo = ILogger.UNIDENTIFIED;
3fd438
 
3fd438
+        SessionContext auditContext = SessionContext.getExistingContext();
3fd438
+
3fd438
+        // create audit context if clientCert exists
3fd438
+        X509Certificate clientCert =
3fd438
+               (X509Certificate) auditContext.get(SessionContext.SSL_CLIENT_CERT);
3fd438
+        // null is okay, as it is not required in case of self-sign;
3fd438
+        // will be checked later
3fd438
+        if (clientCert != null) {
3fd438
+            try {
3fd438
+                createAuditSubjectFromCert(auditContext, clientCert);
3fd438
+            } catch (IOException e) { 
3fd438
+               //unlikely, and not necessarily required at this point
3fd438
+               CMS.debug("CMSUserSignedAuth: authenticate: after createAuditSubjectFromCert call; " + e);
3fd438
+            }
3fd438
+        }
3fd438
+
3fd438
         // ensure that any low-level exceptions are reported
3fd438
         // to the signed audit log and stored as failures
3fd438
         try {
3fd438
@@ -296,8 +313,6 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                 throw new EInvalidCredentials(msg);
3fd438
             }
3fd438
 
3fd438
-            SessionContext auditContext = SessionContext.getExistingContext();
3fd438
-
3fd438
             // authenticate by checking CMC.
3fd438
 
3fd438
             // everything OK.
3fd438
@@ -364,13 +379,13 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                                 }
3fd438
                                 // reset value of auditSignerInfo
3fd438
                                 if (uid != null && !uid.equals(ILogger.UNIDENTIFIED)) {
3fd438
-                                    CMS.debug(method + "setting auditSignerInfo to uid:" + uid.trim());
3fd438
-                                    auditSignerInfo = uid.trim();
3fd438
+                                    //CMS.debug(method + "setting auditSignerInfo to uid:" + uid.trim());
3fd438
+                                    //auditSignerInfo = uid.trim();
3fd438
                                     auditSubjectID = uid.trim();
3fd438
                                     authToken.set(IAuthToken.USER_ID, auditSubjectID);
3fd438
                                 } else if (userid != null && !userid.equals(ILogger.UNIDENTIFIED)) {
3fd438
-                                    CMS.debug(method + "setting auditSignerInfo to userid:" + userid);
3fd438
-                                    auditSignerInfo = userid.trim();
3fd438
+                                    //CMS.debug(method + "setting auditSignerInfo to userid:" + userid);
3fd438
+                                    //auditSignerInfo = userid.trim();
3fd438
                                     auditSubjectID = userid.trim();
3fd438
                                     authToken.set(IAuthToken.USER_ID, auditSubjectID);
3fd438
                                 }
3fd438
@@ -538,16 +553,17 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                                 }
3fd438
 
3fd438
                                 PKCS10 pkcs10 = new PKCS10(ostream.toByteArray(), sigver);
3fd438
-                                // reset value of auditCertSubject
3fd438
+                                // reset value of requestCertSubject
3fd438
                                 X500Name tempName = pkcs10.getSubjectName();
3fd438
                                 CMS.debug(method + "request subject name=" + tempName.toString());
3fd438
                                 if (tempName != null) {
3fd438
-                                    auditCertSubject = tempName.toString().trim();
3fd438
-                                    if (auditCertSubject.equals("")) {
3fd438
-                                        auditCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
3fd438
+                                    requestCertSubject = tempName.toString().trim();
3fd438
+                                    if (requestCertSubject.equals("")) {
3fd438
+                                        requestCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
3fd438
                                     }
3fd438
                                     authToken.set(AuthToken.TOKEN_CERT_SUBJECT,
3fd438
-                                            auditCertSubject/*tempName.toString()*/);
3fd438
+                                            requestCertSubject/*tempName.toString()*/);
3fd438
+                                    auditContext.put(SessionContext.CMC_REQUEST_CERT_SUBJECT, requestCertSubject);
3fd438
                                 }
3fd438
 
3fd438
                                 if (selfSigned) {
3fd438
@@ -632,17 +648,18 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                                 // xxx do we need to do anything else?
3fd438
                                 X509CertInfo certInfo = CMS.getDefaultX509CertInfo();
3fd438
 
3fd438
-                                // reset value of auditCertSubject
3fd438
+                                // reset value of requestCertSubject
3fd438
                                 if (name != null) {
3fd438
                                     String ss = name.getRFC1485();
3fd438
 
3fd438
-                                    CMS.debug(method + "setting auditCertSubject to: " + ss);
3fd438
-                                    auditCertSubject = ss;
3fd438
-                                    if (auditCertSubject.equals("")) {
3fd438
-                                        auditCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
3fd438
+                                    CMS.debug(method + "setting requestCertSubject to: " + ss);
3fd438
+                                    requestCertSubject = ss;
3fd438
+                                    if (requestCertSubject.equals("")) {
3fd438
+                                        requestCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE;
3fd438
                                     }
3fd438
 
3fd438
                                     authToken.set(AuthToken.TOKEN_CERT_SUBJECT, ss);
3fd438
+                                    auditContext.put(SessionContext.CMC_REQUEST_CERT_SUBJECT, requestCertSubject);
3fd438
                                     //authToken.set("uid", uid);
3fd438
                                     //authToken.set("userid", userid);
3fd438
                                 }
3fd438
@@ -696,10 +713,15 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
 
3fd438
                 authToken.set("uid", uid);
3fd438
                 authToken.set("userid", userid);
3fd438
+            } catch (EMissingCredential e) {
3fd438
+                throw e;
3fd438
+            } catch (EInvalidCredentials e) {
3fd438
+                throw e;
3fd438
             } catch (Exception e) {
3fd438
-                CMS.debug(method + e);
3fd438
+                //CMS.debug(method + e);
3fd438
                 //Debug.printStackTrace(e);
3fd438
-                throw new EInvalidCredentials(e.toString());
3fd438
+                //throw new EInvalidCredentials(e.toString());
3fd438
+                throw e;
3fd438
             }
3fd438
 
3fd438
             // For accuracy, make sure revocation by shared secret doesn't
3fd438
@@ -709,11 +731,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                 // store a message in the signed audit log file
3fd438
                 auditMessage = CMS.getLogMessage(
3fd438
                         AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS,
3fd438
-                        auditSubjectID,
3fd438
+                        getAuditSubjectID(),
3fd438
                         ILogger.SUCCESS,
3fd438
                         auditReqType,
3fd438
-                        auditCertSubject,
3fd438
-                        auditSignerInfo);
3fd438
+                        getRequestCertSubject(auditContext),
3fd438
+                        getAuditSignerInfo(auditContext));
3fd438
 
3fd438
                 audit(auditMessage);
3fd438
             } else {
3fd438
@@ -725,17 +747,6 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
             return authToken;
3fd438
         } catch (EMissingCredential eAudit1) {
3fd438
             CMS.debug(method + eAudit1);
3fd438
-            // store a message in the signed audit log file
3fd438
-            auditMessage = CMS.getLogMessage(
3fd438
-                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
3fd438
-                    auditSubjectID,
3fd438
-                    ILogger.FAILURE,
3fd438
-                    auditReqType,
3fd438
-                    auditCertSubject,
3fd438
-                    auditSignerInfo,
3fd438
-                    eAudit1.toString());
3fd438
-
3fd438
-            audit(auditMessage);
3fd438
 
3fd438
             // rethrow the specific exception to be handled later
3fd438
             throw eAudit1;
3fd438
@@ -744,11 +755,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
             // store a message in the signed audit log file
3fd438
             auditMessage = CMS.getLogMessage(
3fd438
                     AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
3fd438
-                    auditSubjectID,
3fd438
+                    getAuditSubjectID(),
3fd438
                     ILogger.FAILURE,
3fd438
                     auditReqType,
3fd438
-                    auditCertSubject,
3fd438
-                    auditSignerInfo,
3fd438
+                    getRequestCertSubject(auditContext),
3fd438
+                    getAuditSignerInfo(auditContext),
3fd438
                     eAudit2.toString());
3fd438
 
3fd438
             audit(auditMessage);
3fd438
@@ -760,11 +771,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
             // store a message in the signed audit log file
3fd438
             auditMessage = CMS.getLogMessage(
3fd438
                     AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
3fd438
-                    auditSubjectID,
3fd438
+                    getAuditSubjectID(),
3fd438
                     ILogger.FAILURE,
3fd438
                     auditReqType,
3fd438
-                    auditCertSubject,
3fd438
-                    auditSignerInfo,
3fd438
+                    getRequestCertSubject(auditContext),
3fd438
+                    getAuditSignerInfo(auditContext),
3fd438
                     eAudit3.toString());
3fd438
 
3fd438
             audit(auditMessage);
3fd438
@@ -776,17 +787,17 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
             // store a message in the signed audit log file
3fd438
             auditMessage = CMS.getLogMessage(
3fd438
                     AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
3fd438
-                    auditSubjectID,
3fd438
+                    getAuditSubjectID(),
3fd438
                     ILogger.FAILURE,
3fd438
                     auditReqType,
3fd438
-                    auditCertSubject,
3fd438
-                    auditSignerInfo,
3fd438
+                    getRequestCertSubject(auditContext),
3fd438
+                    getAuditSignerInfo(auditContext),
3fd438
                     eAudit4.toString());
3fd438
 
3fd438
             audit(auditMessage);
3fd438
 
3fd438
-            // rethrow the specific exception to be handled later
3fd438
-            throw eAudit4;
3fd438
+            // rethrow the exception to be handled later
3fd438
+            throw new EBaseException(eAudit4);
3fd438
         }
3fd438
     }
3fd438
 
3fd438
@@ -935,8 +946,9 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
             SessionContext auditContext, // to capture info in case of failure
3fd438
             AuthToken authToken,
3fd438
             SignedData cmcFullReq)
3fd438
-            throws EBaseException {
3fd438
+            throws EBaseException, EInvalidCredentials, EMissingCredential {
3fd438
         String method = "CMCUserSignedAuth: verifySignerInfo: ";
3fd438
+        String msg = "";
3fd438
         CMS.debug(method + "begins");
3fd438
         EncapsulatedContentInfo ci = cmcFullReq.getContentInfo();
3fd438
         OBJECT_IDENTIFIER id = ci.getContentType();
3fd438
@@ -1001,7 +1013,7 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                     if (cmcFullReq.hasCertificates()) {
3fd438
                         SET certs = cmcFullReq.getCertificates();
3fd438
                         int numCerts = certs.size();
3fd438
-                        java.security.cert.X509Certificate[] x509Certs = new java.security.cert.X509Certificate[1];
3fd438
+                        X509Certificate[] x509Certs = new X509Certificate[1];
3fd438
                         byte[] certByteArray = new byte[0];
3fd438
                         for (int j = 0; j < numCerts; j++) {
3fd438
                             Certificate certJss = (Certificate) certs.elementAt(j);
3fd438
@@ -1029,25 +1041,44 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                         }
3fd438
 
3fd438
                         CMS.debug(method + "start checking signature");
3fd438
-                        String CN = null;
3fd438
                         if (cert == null) {
3fd438
                             // find from certDB
3fd438
                             CMS.debug(method + "verifying signature");
3fd438
                             si.verify(digest, id);
3fd438
                         } else {
3fd438
-                            CMS.debug(method + "found signing cert... verifying");
3fd438
+                            CMS.debug(method + "found CMC signing cert... verifying");
3fd438
+
3fd438
+                            X509Certificate clientCert =
3fd438
+                                    (X509Certificate) auditContext.get(SessionContext.SSL_CLIENT_CERT);
3fd438
+                            // user-signed case requires ssl client authentication
3fd438
+                            if (clientCert == null) {
3fd438
+                                createAuditSubjectFromCert(auditContext, x509Certs[0]);
3fd438
+                                msg = "missing SSL client authentication certificate;";
3fd438
+                                CMS.debug(method + msg);
3fd438
+                                s.close();
3fd438
+                                throw new EMissingCredential(
3fd438
+                                        CMS.getUserMessage("CMS_AUTHENTICATION_NO_CERT"));
3fd438
+                            }
3fd438
+                            netscape.security.x509.X500Name clientPrincipal =
3fd438
+                                    (X500Name) clientCert.getSubjectDN();
3fd438
 
3fd438
-                            // capture auditSubjectID first in case of failure
3fd438
-                            netscape.security.x509.X500Name principal =
3fd438
+                            netscape.security.x509.X500Name cmcPrincipal =
3fd438
                                     (X500Name) x509Certs[0].getSubjectDN();
3fd438
 
3fd438
                             // capture signer principal to be checked against
3fd438
                             // cert subject principal later in CMCOutputTemplate
3fd438
                             // in case of user signed revocation
3fd438
-                            auditContext.put(SessionContext.CMC_SIGNER_PRINCIPAL, principal);
3fd438
-                            CN = principal.getCommonName(); //tempToken.get("userid");
3fd438
-                            CMS.debug(method + " Principal name = " + CN);
3fd438
-                            auditContext.put(SessionContext.USER_ID, CN);
3fd438
+                            auditContext.put(SessionContext.CMC_SIGNER_PRINCIPAL, cmcPrincipal);
3fd438
+                            auditContext.put(SessionContext.CMC_SIGNER_INFO, cmcPrincipal.getCommonName());
3fd438
+
3fd438
+                            // check ssl client cert against cmc signer
3fd438
+                            if (!clientPrincipal.equals(cmcPrincipal)) {
3fd438
+                                msg = "SSL client authentication certificate and CMC signer do not match";
3fd438
+                                CMS.debug(method + msg);
3fd438
+                                s.close();
3fd438
+                                throw new EInvalidCredentials(
3fd438
+                                        CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg);
3fd438
+                            }
3fd438
 
3fd438
                             PublicKey signKey = cert.getPublicKey();
3fd438
                             PrivateKey.Type keyType = null;
3fd438
@@ -1064,10 +1095,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                                 byte publicKeyData[] = ((X509Key) signKey).getEncoded();
3fd438
                                 pubK = PK11ECPublicKey.fromSPKI(/*keyType,*/ publicKeyData);
3fd438
                             } else {
3fd438
-                                CMS.debug(method + "unsupported signature algorithm: " + alg);
3fd438
+                                msg = "unsupported signature algorithm: " + alg;
3fd438
+                                CMS.debug(method +  msg);
3fd438
                                 s.close();
3fd438
                                 throw new EInvalidCredentials(
3fd438
-                                        CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
3fd438
+                                        CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg);
3fd438
                             }
3fd438
 
3fd438
                             String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token",
3fd438
@@ -1095,9 +1127,10 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                         // ...or not;  I think it just checks usage and
3fd438
                         // validity, but not revocation status
3fd438
                         if (!cm.isCertValid(certByteArray, true, CryptoManager.CertUsage.SSLClient)) {
3fd438
-                            CMS.debug(method + "CMC signature failed to be verified");
3fd438
+                            msg = "CMC signing cert is invalid";
3fd438
+                            CMS.debug(method + msg);
3fd438
                             s.close();
3fd438
-                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
3fd438
+                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg);
3fd438
                         } else {
3fd438
                             CMS.debug(method + "CMC signature verified; but signer not yet;");
3fd438
                         }
3fd438
@@ -1105,28 +1138,28 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
 
3fd438
                         // now check revocation status of the cert
3fd438
                         if (CMS.isRevoked(x509Certs)) {
3fd438
-                            CMS.debug(method + "CMC signing cert is a revoked certificate");
3fd438
+                            msg = "CMC signing cert is a revoked certificate";
3fd438
+                            CMS.debug(method + msg);
3fd438
                             s.close();
3fd438
-                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
3fd438
+                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg);
3fd438
                         }
3fd438
                         try { //do this again anyways
3fd438
                             cert.checkValidity();
3fd438
                         } catch (CertificateExpiredException e) {
3fd438
-                            CMS.debug(method + "CMC signing cert is an expired certificate");
3fd438
+                            msg = "CMC signing cert is an expired certificate";
3fd438
+                            CMS.debug(method + msg);
3fd438
                             s.close();
3fd438
-                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
3fd438
+                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg);
3fd438
                         } catch (Exception e) {
3fd438
                             CMS.debug(method + e.toString());
3fd438
                             s.close();
3fd438
-                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
3fd438
+                            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + e.toString());
3fd438
                         }
3fd438
 
3fd438
                         IAuthToken tempToken = new AuthToken(null);
3fd438
-/*
3fd438
                         netscape.security.x509.X500Name tempPrincipal = (X500Name) x509Certs[0].getSubjectDN();
3fd438
                         String CN = tempPrincipal.getCommonName(); //tempToken.get("userid");
3fd438
                         CMS.debug(method + " Principal name = " + CN);
3fd438
-*/
3fd438
 
3fd438
                         BigInteger certSerial = x509Certs[0].getSerialNumber();
3fd438
                         CMS.debug(method + " verified cert serial=" + certSerial.toString());
3fd438
@@ -1137,7 +1170,9 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                         return tempToken;
3fd438
 
3fd438
                     } else {
3fd438
-                        CMS.debug(method + "no certificate found in cmcFullReq");
3fd438
+                        msg = "no certificate found in cmcFullReq";
3fd438
+                        CMS.debug(method + msg);
3fd438
+                        throw new EMissingCredential(msg);
3fd438
                     }
3fd438
                 } else if (sid.getType().equals(SignerIdentifier.SUBJECT_KEY_IDENTIFIER)) {
3fd438
                     CMS.debug(method + "SignerIdentifier type: SUBJECT_KEY_IDENTIFIER");
3fd438
@@ -1150,19 +1185,20 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
                     s.close();
3fd438
                     return tempToken;
3fd438
                 } else {
3fd438
-                    CMS.debug(method + "unsupported SignerIdentifier type");
3fd438
+                    msg = "unsupported SignerIdentifier type";
3fd438
+                    CMS.debug(method + msg);
3fd438
+                    throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg);
3fd438
                 }
3fd438
             } //for
3fd438
 
3fd438
+        } catch (EMissingCredential e) {
3fd438
+            throw e;
3fd438
+        } catch (EInvalidCredentials e) {
3fd438
+            throw e;
3fd438
         } catch (InvalidBERException e) {
3fd438
-            CMS.debug(method + e.toString());
3fd438
-        } catch (IOException e) {
3fd438
-            CMS.debug(method + e.toString());
3fd438
-        } catch (NotInitializedException e) {
3fd438
-            CMS.debug(method + e.toString());
3fd438
+            CMS.debug(method + e);
3fd438
         } catch (Exception e) {
3fd438
-            CMS.debug(method + e.toString());
3fd438
-            throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL"));
3fd438
+            CMS.debug(method + e);
3fd438
         } finally {
3fd438
             if ((tokenSwitched == true) && (savedToken != null)) {
3fd438
                 cm.setThreadToken(savedToken);
3fd438
@@ -1173,6 +1209,21 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
 
3fd438
     }
3fd438
 
3fd438
+    private void createAuditSubjectFromCert (
3fd438
+            SessionContext auditContext,
3fd438
+            X509Certificate cert)
3fd438
+            throws IOException {
3fd438
+        String method = "CMCUserSignedAuth:createAuditSubjectFromCert: ";
3fd438
+
3fd438
+        // capture auditSubjectID first in case of failure
3fd438
+        netscape.security.x509.X500Name principal =
3fd438
+                (X500Name) cert.getSubjectDN();
3fd438
+
3fd438
+        String CN = principal.getCommonName();
3fd438
+        CMS.debug(method + " Principal name = " + CN);
3fd438
+        auditContext.put(SessionContext.USER_ID, CN);
3fd438
+    }
3fd438
+
3fd438
     public String[] getExtendedPluginInfo(Locale locale) {
3fd438
         return null;
3fd438
     }
3fd438
@@ -1274,7 +1325,7 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
      *
3fd438
      * @return id string containing the signed audit log message SubjectID
3fd438
      */
3fd438
-    private String auditSubjectID() {
3fd438
+    private String getAuditSubjectID() {
3fd438
         // if no signed audit object exists, bail
3fd438
         if (mSignedAuditLogger == null) {
3fd438
             return null;
3fd438
@@ -1299,4 +1350,21 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
3fd438
 
3fd438
         return subjectID;
3fd438
     }
3fd438
+
3fd438
+    private String getAuditSignerInfo(SessionContext auditContext) {
3fd438
+        String signerSubject = (String)auditContext.get(SessionContext.CMC_SIGNER_INFO);
3fd438
+        if (signerSubject == null)
3fd438
+            signerSubject = "$Unidentified$";
3fd438
+
3fd438
+        return signerSubject;
3fd438
+    }
3fd438
+
3fd438
+    private String getRequestCertSubject(SessionContext auditContext) {
3fd438
+        String certSubject = (String)auditContext.get(SessionContext.CMC_REQUEST_CERT_SUBJECT);
3fd438
+        if (certSubject == null)
3fd438
+            certSubject = "$Unidentified$";
3fd438
+
3fd438
+        return certSubject;
3fd438
+    }
3fd438
+
3fd438
 }
3fd438
diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java
3fd438
index 33cc7a9..030995a 100644
3fd438
--- a/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java
3fd438
+++ b/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java
3fd438
@@ -219,12 +219,14 @@ public class UniqueKeyConstraint extends EnrollConstraint {
3fd438
                         Date origNotAfter = null;
3fd438
                         boolean first = true;
3fd438
                         while (e != null && e.hasMoreElements()) {
3fd438
+                            CMS.debug(method +  msg);
3fd438
                             ICertRecord rec = e.nextElement();
3fd438
                             BigInteger serial = rec.getSerialNumber();
3fd438
+                            msg = msg + "existing cert with same key found: " + serial.toString() + ";";
3fd438
 
3fd438
                             if (rec.getStatus().equals(ICertRecord.STATUS_REVOKED)
3fd438
                                     || rec.getStatus().equals(ICertRecord.STATUS_REVOKED_EXPIRED)) {
3fd438
-                                msg = msg + "revoked cert cannot be renewed: serial=" + serial.toString() + ";";
3fd438
+                                msg = msg + "revoked cert cannot be renewed;";
3fd438
                                 CMS.debug(method + msg);
3fd438
                                 rejected = true;
3fd438
                                 // this has to break
3fd438
@@ -232,7 +234,7 @@ public class UniqueKeyConstraint extends EnrollConstraint {
3fd438
                             }
3fd438
                             if (!rec.getStatus().equals(ICertRecord.STATUS_VALID)
3fd438
                                     && !rec.getStatus().equals(ICertRecord.STATUS_EXPIRED)) {
3fd438
-                                CMS.debug(method + "invalid cert cannot be renewed; continue:" + serial.toString());
3fd438
+                                CMS.debug(method + "invalid cert cannot be renewed; continue;" + serial.toString());
3fd438
                                 // can still find another one to renew
3fd438
                                 continue;
3fd438
                             }
3fd438
@@ -297,7 +299,7 @@ public class UniqueKeyConstraint extends EnrollConstraint {
3fd438
         } // (size > 0)
3fd438
 
3fd438
         if (rejected == true) {
3fd438
-            CMS.debug(method + " rejected");
3fd438
+            CMS.debug(method + " rejected: " + msg);
3fd438
             throw new ERejectException(msg);
3fd438
         } else {
3fd438
             CMS.debug(method + " approved");
3fd438
diff --git a/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java b/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java
3fd438
index 9dc7470..65dc06a 100644
3fd438
--- a/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java
3fd438
+++ b/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java
3fd438
@@ -843,6 +843,10 @@ public abstract class CMSServlet extends HttpServlet {
3fd438
      * get ssl client authenticated certificate
3fd438
      */
3fd438
     protected X509Certificate getSSLClientCertificate(HttpServletRequest httpReq) throws EBaseException {
3fd438
+        return getSSLClientCertificate(httpReq, true);
3fd438
+    }
3fd438
+
3fd438
+    protected X509Certificate getSSLClientCertificate(HttpServletRequest httpReq, boolean clientCertRequired) throws EBaseException {
3fd438
 
3fd438
         X509Certificate cert = null;
3fd438
 
3fd438
@@ -855,7 +859,11 @@ public abstract class CMSServlet extends HttpServlet {
3fd438
         X509Certificate[] allCerts = (X509Certificate[]) httpReq.getAttribute(CERT_ATTR);
3fd438
 
3fd438
         if (allCerts == null || allCerts.length == 0) {
3fd438
-            throw new EBaseException("You did not provide a valid certificate for this operation");
3fd438
+            if (!clientCertRequired) {
3fd438
+                return null;
3fd438
+            } else {
3fd438
+                throw new EBaseException("You did not provide a valid certificate for this operation");
3fd438
+            }
3fd438
         }
3fd438
 
3fd438
         cert = allCerts[0];
3fd438
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
3fd438
index 330b5ff..73195e9 100644
3fd438
--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java
3fd438
+++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java
3fd438
@@ -19,6 +19,7 @@ package com.netscape.cms.servlet.profile;
3fd438
 
3fd438
 import java.io.InputStream;
3fd438
 import java.io.OutputStream;
3fd438
+import java.security.cert.X509Certificate;
3fd438
 import java.util.Enumeration;
3fd438
 import java.util.Locale;
3fd438
 
3fd438
@@ -169,6 +170,12 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
3fd438
         String authMgrID = authenticator.getName();
3fd438
         SessionContext sc = SessionContext.getContext();
3fd438
 
3fd438
+        X509Certificate clientCert =
3fd438
+                getSSLClientCertificate(request, false /*cert may not be required*/);
3fd438
+        if (clientCert != null) {
3fd438
+           sc.put(SessionContext.SSL_CLIENT_CERT, clientCert);
3fd438
+        }
3fd438
+
3fd438
         try {
3fd438
             authToken = authenticator.authenticate(credentials);
3fd438
             if (sc != null) {
3fd438
diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties
3fd438
index 9490098..5e51440 100644
3fd438
--- a/base/server/cmsbundle/src/LogMessages.properties
3fd438
+++ b/base/server/cmsbundle/src/LogMessages.properties
3fd438
@@ -2208,10 +2208,10 @@ LOGGING_SIGNED_AUDIT_CMC_SIGNED_REQUEST_SIG_VERIFY_5=
3fd438
 #   are submitted and signature is verified
3fd438
 # ReqType must be the request type (enrollment, or revocation)
3fd438
 # CertSubject must be the certificate subject name of the certificate request
3fd438
-# SignerInfo must be a unique String representation for the signer
3fd438
+# CMCSignerInfo must be a unique String representation for the CMC request signer
3fd438
 #
3fd438
 LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS_5=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}] User signed CMC request signature verification success
3fd438
-LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE_6=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}][info={5}] User signed CMC request signature verification failure
3fd438
+LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE_6=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][CMCSignerInfo={4}][info={5}] User signed CMC request signature verification failure
3fd438
 
3fd438
 # LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST 
3fd438
 # - used for TPS to TKS to get random challenge data
3fd438
-- 
3fd438
1.8.3.1
3fd438