Bug 406755, EV certs not recognized as EV with some cross-certification scenarios r=rrelyea, blocking1.9=dsicore
authorkaie@kuix.de
Sun, 16 Mar 2008 06:42:32 -0700
changeset 13140 9bc5ecdeed69763e1d43beccdf370d034791d9be
parent 13139 a51a433f63f22d33b87f042d70ba656e56c17b26
child 13141 991ba8cf49057d2f6f52abee014bf400e2224423
push idunknown
push userunknown
push dateunknown
reviewersrrelyea
bugs406755
milestone1.9b5pre
Bug 406755, EV certs not recognized as EV with some cross-certification scenarios r=rrelyea, blocking1.9=dsicore
security/manager/ssl/src/nsIdentityChecking.cpp
security/manager/ssl/src/nsNSSComponent.cpp
--- a/security/manager/ssl/src/nsIdentityChecking.cpp
+++ b/security/manager/ssl/src/nsIdentityChecking.cpp
@@ -40,16 +40,17 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsStreamUtils.h"
 #include "nsNetUtil.h"
 #include "nsILineInputStream.h"
 #include "nsPromiseFlatString.h"
 #include "nsTArray.h"
 
 #include "cert.h"
+#include "base64.h"
 #include "nsNSSComponent.h"
 #include "nsNSSIOLayer.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSCleaner.h"
 
 #ifdef DEBUG
 #ifndef PSM_ENABLE_TEST_EV_ROOTS
 #define PSM_ENABLE_TEST_EV_ROOTS
@@ -69,74 +70,116 @@ NSSCleanupAutoPtrClass_WithParam(SECItem
 
 struct nsMyTrustedEVInfo
 {
   char *dotted_oid;
   char *oid_name; // Set this to null to signal an invalid structure,
                   // (We can't have an empty list, so we'll use a dummy entry)
   SECOidTag oid_tag;
   char *ev_root_sha1_fingerprint;
+  char *issuer_base64;
+  char *serial_base64;
+  CERTCertificate *cert;
 };
 
 static struct nsMyTrustedEVInfo myTrustedEVInfos[] = {
   {
     // OU=Go Daddy Class 2 Certification Authority,O=\"The Go Daddy Group, Inc.\",C=US
     "2.16.840.1.114413.1.7.23.3",
     "Go Daddy EV OID a",
     SEC_OID_UNKNOWN,
     "27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4",
+    "MGMxCzAJBgNVBAYTAlVTMSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIElu"
+    "Yy4xMTAvBgNVBAsTKEdvIERhZGR5IENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRo"
+    "b3JpdHk=",
+    "AA==",
+    nsnull
   },
   {
     // E=info@valicert.com,CN=http://www.valicert.com/,OU=ValiCert Class 2 Policy Validation Authority,O=\"ValiCert, Inc.\",L=ValiCert Validation Network
     "2.16.840.1.114413.1.7.23.3",
     "Go Daddy EV OID a",
     SEC_OID_UNKNOWN,
     "31:7A:2A:D0:7F:2B:33:5E:F5:A1:C3:4E:4B:57:E8:B7:D8:F1:FC:A6",
+    "MIG7MSQwIgYDVQQHExtWYWxpQ2VydCBWYWxpZGF0aW9uIE5ldHdvcmsxFzAVBgNV"
+    "BAoTDlZhbGlDZXJ0LCBJbmMuMTUwMwYDVQQLEyxWYWxpQ2VydCBDbGFzcyAyIFBv"
+    "bGljeSBWYWxpZGF0aW9uIEF1dGhvcml0eTEhMB8GA1UEAxMYaHR0cDovL3d3dy52"
+    "YWxpY2VydC5jb20vMSAwHgYJKoZIhvcNAQkBFhFpbmZvQHZhbGljZXJ0LmNvbQ==",
+    "AQ==",
+    nsnull
   },
   {
     // E=info@valicert.com,CN=http://www.valicert.com/,OU=ValiCert Class 2 Policy Validation Authority,O=\"ValiCert, Inc.\",L=ValiCert Validation Network
     "2.16.840.1.114414.1.7.23.3",
     "Go Daddy EV OID b",
     SEC_OID_UNKNOWN,
     "31:7A:2A:D0:7F:2B:33:5E:F5:A1:C3:4E:4B:57:E8:B7:D8:F1:FC:A6",
+    "MIG7MSQwIgYDVQQHExtWYWxpQ2VydCBWYWxpZGF0aW9uIE5ldHdvcmsxFzAVBgNV"
+    "BAoTDlZhbGlDZXJ0LCBJbmMuMTUwMwYDVQQLEyxWYWxpQ2VydCBDbGFzcyAyIFBv"
+    "bGljeSBWYWxpZGF0aW9uIEF1dGhvcml0eTEhMB8GA1UEAxMYaHR0cDovL3d3dy52"
+    "YWxpY2VydC5jb20vMSAwHgYJKoZIhvcNAQkBFhFpbmZvQHZhbGljZXJ0LmNvbQ==",
+    "AQ==",
+    nsnull
   },
   {
     // OU=Starfield Class 2 Certification Authority,O=\"Starfield Technologies, Inc.\",C=US
     "2.16.840.1.114414.1.7.23.3",
     "Go Daddy EV OID b",
     SEC_OID_UNKNOWN,
     "AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
+    "MGgxCzAJBgNVBAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVz"
+    "LCBJbmMuMTIwMAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9u"
+    "IEF1dGhvcml0eQ==",
+    "AA==",
+    nsnull
   },
   {
     // CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US
     "2.16.840.1.114412.2.1",
     "DigiCert EV OID",
     SEC_OID_UNKNOWN,
-    "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25"
+    "5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
+    "MGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT"
+    "EHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJh"
+    "bmNlIEVWIFJvb3QgQ0E=",
+    "AqxcJmoLQJuPC3nyrkYldw==",
+    nsnull
   },
   {
     // CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM
     "1.3.6.1.4.1.8024.0.2.100.1.2",
     "Quo Vadis EV OID",
     SEC_OID_UNKNOWN,
-    "CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7"
+    "CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7",
+    "MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYD"
+    "VQQDExJRdW9WYWRpcyBSb290IENBIDI=",
+    "BQk=",
+    nsnull
   },
   {
     // OU=Class 3 Public Primary Certification Authority,O=\"VeriSign, Inc.\",C=US
     "2.16.840.1.113733.1.7.23.6",
     "Verisign EV OID",
     SEC_OID_UNKNOWN,
-    "74:2C:31:92:E6:07:E4:24:EB:45:49:54:2B:E1:BB:C5:3E:61:74:E2"
+    "74:2C:31:92:E6:07:E4:24:EB:45:49:54:2B:E1:BB:C5:3E:61:74:E2",
+    "MF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UE"
+    "CxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0"
+    "eQ==",
+    "cLrkHRDZKTS2OMp7A8y6vw==",
+    nsnull
   },
   {
     // OU=Sample Certification Authority,O=\"Sample, Inc.\",C=US
     "0.0.0.0",
     0, // for real entries use a string like "Sample INVALID EV OID"
     SEC_OID_UNKNOWN,
     "00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33"
+    "Cg==",
+    "Cg==",
+    nsnull
   }
 };
 
 static SECOidTag
 register_oid(const SECItem *oid_item, const char *oid_name)
 {
   if (!oid_item)
     return SEC_OID_UNKNOWN;
@@ -160,23 +203,30 @@ public:
 };
 
 nsMyTrustedEVInfoClass::nsMyTrustedEVInfoClass()
 {
   dotted_oid = nsnull;
   oid_name = nsnull;
   oid_tag = SEC_OID_UNKNOWN;
   ev_root_sha1_fingerprint = nsnull;
+  issuer_base64 = nsnull;
+  serial_base64 = nsnull;
+  cert = nsnull;
 }
 
 nsMyTrustedEVInfoClass::~nsMyTrustedEVInfoClass()
 {
   free(dotted_oid);
   free(oid_name);
   free(ev_root_sha1_fingerprint);
+  free(issuer_base64);
+  free(serial_base64);
+  if (cert)
+    CERT_DestroyCertificate(cert);
 }
 
 typedef nsTArray< nsMyTrustedEVInfoClass* > testEVArray; 
 static testEVArray *testEVInfos;
 static PRBool testEVInfosLoaded = PR_FALSE;
 #endif
 
 static PRBool isEVMatch(SECOidTag policyOIDTag, 
@@ -253,20 +303,20 @@ loadTestEVInfos()
    * completely empty lines are ignored
    * lines that start with the # char are ignored
    */
 
   int line_counter = 0;
   PRBool found_error = PR_FALSE;
 
   enum { 
-    pos_fingerprint, pos_readable_oid
+    pos_fingerprint, pos_readable_oid, pos_issuer, pos_serial
   } reader_position = pos_fingerprint;
 
-  nsCString fingerprint, readable_oid;
+  nsCString fingerprint, readable_oid, issuer, serial;
 
   while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
     ++line_counter;
     if (buffer.IsEmpty() || buffer.First() == '#') {
       continue;
     }
 
     PRInt32 seperatorIndex = buffer.FindChar(' ', 0);
@@ -284,30 +334,69 @@ loadTestEVInfos()
         descriptor.EqualsLiteral(("1_fingerprint"))) {
       fingerprint = data;
       reader_position = pos_readable_oid;
       continue;
     }
     else if (reader_position == pos_readable_oid &&
         descriptor.EqualsLiteral(("2_readable_oid"))) {
       readable_oid = data;
+      reader_position = pos_issuer;
+    }
+    else if (reader_position == pos_issuer &&
+        descriptor.EqualsLiteral(("3_issuer"))) {
+      issuer = data;
+      reader_position = pos_serial;
+    }
+    else if (reader_position == pos_readable_oid &&
+        descriptor.EqualsLiteral(("4_serial"))) {
+      serial = data;
       reader_position = pos_fingerprint;
     }
     else {
       found_error = PR_TRUE;
       break;
     }
 
     nsMyTrustedEVInfoClass *temp_ev = new nsMyTrustedEVInfoClass;
     if (!temp_ev)
       return;
 
     temp_ev->ev_root_sha1_fingerprint = strdup(fingerprint.get());
     temp_ev->oid_name = strdup(readable_oid.get());
     temp_ev->dotted_oid = strdup(readable_oid.get());
+    temp_ev->issuer_base64 = strdup(issuer.get());
+    temp_ev->serial_base64 = strdup(serial.get());
+
+    SECStatus rv;
+    CERTIssuerAndSN ias;
+
+    rv = ATOB_ConvertAsciiToItem(&ias.derIssuer, const_cast<char*>(temp_ev->issuer_base64));
+    NS_ASSERTION(rv==SECSuccess, "error converting ascii to binary.");
+    rv = ATOB_ConvertAsciiToItem(&ias.serialNumber, const_cast<char*>(temp_ev->serial_base64));
+    NS_ASSERTION(rv==SECSuccess, "error converting ascii to binary.");
+
+    temp_ev->cert = CERT_FindCertByIssuerAndSN(nsnull, &ias);
+    NS_ASSERTION(temp_ev->cert, "Could not find EV root in NSS storage");
+
+    if (!temp_ev->cert)
+      return;
+
+    nsNSSCertificate c(temp_ev->cert);
+    nsAutoString fingerprint;
+    c.GetSha1Fingerprint(fingerprint);
+
+    NS_ConvertASCIItoUTF16 sha1(temp_ev->ev_root_sha1_fingerprint);
+
+    if (sha1 != fingerprint) {
+      NS_ASSERTION(sha1 == fingerprint, "found EV root with unexpected SHA1 mismatch");
+      CERT_DestroyCertificate(temp_ev->cert);
+      temp_ev->cert = nsnull;
+      return;
+    }
 
     SECItem ev_oid_item;
     ev_oid_item.data = nsnull;
     ev_oid_item.len = 0;
     SECStatus srv = SEC_StringToOID(nsnull, &ev_oid_item,
                                     readable_oid.get(), readable_oid.Length());
     if (srv != SECSuccess) {
       delete temp_ev;
@@ -347,16 +436,42 @@ isEVPolicyInExternalDebugRootsFile(SECOi
     if (policyOIDTag == ev->oid_tag)
       return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
 static PRBool 
+getRootsForOidFromExternalRootsFile(CERTCertList* certList, 
+                                    SECOidTag policyOIDTag)
+{
+  if (!testEVInfos)
+    return PR_FALSE;
+
+  char *env_val = getenv("ENABLE_TEST_EV_ROOTS_FILE");
+  if (!env_val)
+    return PR_FALSE;
+    
+  int enabled_val = atoi(env_val);
+  if (!enabled_val)
+    return PR_FALSE;
+
+  for (size_t i=0; i<testEVInfos->Length(); ++i) {
+    nsMyTrustedEVInfoClass *ev = testEVInfos->ElementAt(i);
+    if (!ev)
+      continue;
+    if (policyOIDTag == ev->oid_tag)
+      CERT_AddCertToListTail(certList, ev->cert);
+  }
+
+  return PR_FALSE;
+}
+
+static PRBool 
 isEVMatchInExternalDebugRootsFile(SECOidTag policyOIDTag, 
                                   CERTCertificate *rootCert)
 {
   if (!testEVInfos)
     return PR_FALSE;
 
   if (!rootCert)
     return PR_FALSE;
@@ -397,16 +512,37 @@ isEVPolicy(SECOidTag policyOIDTag)
   if (isEVPolicyInExternalDebugRootsFile(policyOIDTag)) {
     return PR_TRUE;
   }
 #endif
 
   return PR_FALSE;
 }
 
+static CERTCertList*
+getRootsForOid(SECOidTag oid_tag)
+{
+  CERTCertList *certList = CERT_NewCertList();
+  if (!certList)
+    return nsnull;
+
+  for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
+    nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
+    if (!entry.oid_name) // invalid or placeholder list entry
+      continue;
+    if (entry.oid_tag == oid_tag)
+      CERT_AddCertToListTail(certList, entry.cert);
+  }
+
+#ifdef PSM_ENABLE_TEST_EV_ROOTS
+  getRootsForOidFromExternalRootsFile(certList, oid_tag);
+#endif
+  return certList;
+}
+
 static PRBool 
 isApprovedForEV(SECOidTag policyOIDTag, CERTCertificate *rootCert)
 {
   if (!rootCert)
     return PR_FALSE;
 
   for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
     nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
@@ -429,16 +565,43 @@ isApprovedForEV(SECOidTag policyOIDTag, 
 PRStatus PR_CALLBACK
 nsNSSComponent::IdentityInfoInit()
 {
   for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
     nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
     if (!entry.oid_name) // invalid or placeholder list entry
       continue;
 
+    SECStatus rv;
+    CERTIssuerAndSN ias;
+
+    rv = ATOB_ConvertAsciiToItem(&ias.derIssuer, const_cast<char*>(entry.issuer_base64));
+    NS_ASSERTION(rv==SECSuccess, "error converting ascii to binary.");
+    rv = ATOB_ConvertAsciiToItem(&ias.serialNumber, const_cast<char*>(entry.serial_base64));
+    NS_ASSERTION(rv==SECSuccess, "error converting ascii to binary.");
+
+    entry.cert = CERT_FindCertByIssuerAndSN(nsnull, &ias);
+    NS_ASSERTION(entry.cert, "Could not find EV root in NSS storage");
+
+    if (!entry.cert)
+      continue;
+
+    nsNSSCertificate c(entry.cert);
+    nsAutoString fingerprint;
+    c.GetSha1Fingerprint(fingerprint);
+
+    NS_ConvertASCIItoUTF16 sha1(entry.ev_root_sha1_fingerprint);
+
+    if (sha1 != fingerprint) {
+      NS_ASSERTION(sha1 == fingerprint, "found EV root with unexpected SHA1 mismatch");
+      CERT_DestroyCertificate(entry.cert);
+      entry.cert = nsnull;
+      continue;
+    }
+
     SECItem ev_oid_item;
     ev_oid_item.data = nsnull;
     ev_oid_item.len = 0;
     SECStatus srv = SEC_StringToOID(nsnull, &ev_oid_item, 
                                     entry.dotted_oid, 0);
     if (srv != SECSuccess)
       continue;
 
@@ -582,43 +745,91 @@ nsNSSCertificate::hasValidEVOidTag(SECOi
   SECOidTag oid_tag;
   SECStatus rv = getFirstEVPolicy(mCert, oid_tag);
   if (rv != SECSuccess)
     return NS_OK;
 
   if (oid_tag == SEC_OID_UNKNOWN) // not in our list of OIDs accepted for EV
     return NS_OK;
 
+  CERTCertList *rootList = getRootsForOid(oid_tag);
+  CERTCertListCleaner rootListCleaner();
+
+  CERTRevocationMethodIndex preferedRevMethods[1] = { 
+    cert_revocation_method_ocsp
+  };
+
+  PRUint64 revMethodFlags = 
+    CERT_REV_M_TEST_USING_THIS_METHOD
+    | CERT_REV_M_ALLOW_NETWORK_FETCHING
+    | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE
+    | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE
+    | CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO
+    | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO;
+
+  PRUint64 revMethodIndependentFlags = 
+    CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST
+    | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE;
+
+  PRUint64 methodFlags[2];
+  methodFlags[cert_revocation_method_crl] = revMethodFlags;
+  methodFlags[cert_revocation_method_ocsp] = revMethodFlags;
+
+  CERTRevocationFlags rev;
+
+  rev.leafTests.number_of_defined_methods = cert_revocation_method_ocsp +1;
+  rev.leafTests.cert_rev_flags_per_method = methodFlags;
+  rev.leafTests.number_of_preferred_methods = 1;
+  rev.leafTests.preferred_methods = preferedRevMethods;
+  rev.leafTests.cert_rev_method_independent_flags =
+    revMethodIndependentFlags;
+
+  rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp +1;
+  rev.leafTests.cert_rev_flags_per_method = methodFlags;
+  rev.chainTests.number_of_preferred_methods = 1;
+  rev.chainTests.preferred_methods = preferedRevMethods;
+  rev.chainTests.cert_rev_method_independent_flags =
+    revMethodIndependentFlags;
+
   CERTValInParam cvin[3];
   cvin[0].type = cert_pi_policyOID;
   cvin[0].value.arraySize = 1; 
   cvin[0].value.array.oids = &oid_tag;
 
   cvin[1].type = cert_pi_revocationFlags;
-  cvin[1].value.scalar.ul = CERT_REV_FAIL_SOFT_CRL
-                            | CERT_REV_FLAG_CRL
-                            | CERT_REV_FLAG_OCSP
-                            ;
-  cvin[2].type = cert_pi_end;
+  cvin[1].value.pointer.revocation = &rev;
+
+  cvin[2].type = cert_pi_trustAnchors;
+  cvin[2].value.pointer.chain = rootList;
+
+  cvin[3].type = cert_pi_end;
 
   CERTValOutParam cvout[2];
   cvout[0].type = cert_po_trustAnchor;
   cvout[1].type = cert_po_end;
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("calling CERT_PKIXVerifyCert nss cert %p\n", mCert));
   rv = CERT_PKIXVerifyCert(mCert, certificateUsageSSLServer,
                            cvin, cvout, nsnull);
   if (rv != SECSuccess)
     return NS_OK;
 
   CERTCertificate *issuerCert = cvout[0].value.pointer.cert;
   CERTCertificateCleaner issuerCleaner(issuerCert);
 
-  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CERT_PKIXVerifyCert returned success, issuer: %s\n", 
-    issuerCert->subjectName));
+#ifdef PR_LOGGING
+  if (PR_LOG_TEST(gPIPNSSLog, PR_LOG_DEBUG)) {
+    nsNSSCertificate ic(issuerCert);
+    nsAutoString fingerprint;
+    ic.GetSha1Fingerprint(fingerprint);
+    NS_LossyConvertUTF16toASCII fpa(fingerprint);
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CERT_PKIXVerifyCert returned success, issuer: %s, SHA1: %s\n", 
+      issuerCert->subjectName, fpa.get()));
+  }
+#endif
 
   validEV = isApprovedForEV(oid_tag, issuerCert);
   if (validEV)
     resultOidTag = oid_tag;
  
   return NS_OK;
 }
 
@@ -695,16 +906,25 @@ nsNSSComponent::EnsureIdentityInfoLoaded
   PRStatus rv = PR_CallOnce(&mIdentityInfoCallOnce, IdentityInfoInit);
   return (rv == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; 
 }
 
 // only called during shutdown
 void
 nsNSSComponent::CleanupIdentityInfo()
 {
+  nsNSSShutDownPreventionLock locker;
+  for (size_t iEV=0; iEV < (sizeof(myTrustedEVInfos)/sizeof(nsMyTrustedEVInfo)); ++iEV) {
+    nsMyTrustedEVInfo &entry = myTrustedEVInfos[iEV];
+    if (entry.cert) {
+      CERT_DestroyCertificate(entry.cert);
+      entry.cert = nsnull;
+    }
+  }
+
 #ifdef PSM_ENABLE_TEST_EV_ROOTS
   if (testEVInfosLoaded) {
     testEVInfosLoaded = PR_FALSE;
     if (testEVInfos) {
       for (size_t i = 0; i<testEVInfos->Length(); ++i) {
         delete testEVInfos->ElementAt(i);
       }
       testEVInfos->Clear();
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -1679,27 +1679,26 @@ nsNSSComponent::ShutdownNSS()
     if (mPrefBranch) {
       nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefBranch);
       pbi->RemoveObserver("security.", this);
     }
 
     ShutdownSmartCardThreads();
     SSL_ClearSessionCache();
     UnloadLoadableRoots();
+    CleanupIdentityInfo();
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("evaporating psm resources\n"));
     mShutdownObjectList->evaporateAllNSSResources();
     if (SECSuccess != ::NSS_Shutdown()) {
       PR_LOG(gPIPNSSLog, PR_LOG_ALWAYS, ("NSS SHUTDOWN FAILURE\n"));
       rv = NS_ERROR_FAILURE;
     }
     else {
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS shutdown =====>> OK <<=====\n"));
     }
-
-    CleanupIdentityInfo();
   }
 
   return rv;
 }
  
 NS_IMETHODIMP
 nsNSSComponent::Init()
 {