Bug 307081 - Make nsIClientAuthDialogs::ChooseCertificate() pass an nsIArray of nsIX509Certs, not strings. r=kats,keeler draft
authorCykesiopka <cykesiopka.bmo@gmail.com>
Fri, 24 Jun 2016 00:12:16 -0700
changeset 381114 52a86543ce4f32dc20f7b456f367e668ebd58d33
parent 381113 ab518fe6a325fe94e4d6583d282c055efb842e2d
child 523885 3dd490d90bdcbb27d064d7e5aa5a9f30860727d9
push id21395
push usercykesiopka.bmo@gmail.com
push dateFri, 24 Jun 2016 07:13:10 +0000
reviewerskats, keeler
bugs307081
milestone50.0a1
Bug 307081 - Make nsIClientAuthDialogs::ChooseCertificate() pass an nsIArray of nsIX509Certs, not strings. r=kats,keeler This provides implementations of ChooseCertificate() with more flexibility, and allows callers of ChooseCertificate() to be less complex. A portion of this work involves reimplementing nsNSSCertificate::FormatUIStrings() in JS and improving UI strings for l10n. MozReview-Commit-ID: CE7Uc2ntwmZ
mobile/android/components/NSSDialogService.js
mobile/android/locales/en-US/chrome/pippki.properties
security/manager/locales/en-US/chrome/pippki/pippki.properties
security/manager/pki/nsNSSDialogs.cpp
security/manager/pki/resources/content/clientauthask.js
security/manager/ssl/nsIClientAuthDialogs.idl
security/manager/ssl/nsNSSIOLayer.cpp
--- a/mobile/android/components/NSSDialogService.js
+++ b/mobile/android/components/NSSDialogService.js
@@ -138,36 +138,78 @@ NSSDialogs.prototype = {
                           ["certmgr.begins", aCert.validity.notBeforeLocalDay,
                            "certmgr.expires", aCert.validity.notAfterLocalDay])})
      .addLabel({ label: this.certInfoSection("certmgr.fingerprints.label",
                           ["certmgr.certdetail.sha256fingerprint", aCert.sha256Fingerprint,
                            "certmgr.certdetail.sha1fingerprint", aCert.sha1Fingerprint], false) });
     this.showPrompt(p);
   },
 
+  /**
+   * Returns a list of details of the given cert relevant for TLS client
+   * authentication.
+   *
+   * @param {nsIX509Cert} cert Cert to get the details of.
+   * @returns {String} <br/> delimited list of details.
+   */
+  getCertDetails: function(cert) {
+    let detailLines = [
+      this.formatString("clientAuthAsk.issuedTo", [cert.subjectName]),
+      this.formatString("clientAuthAsk.serial", [cert.serialNumber]),
+      this.formatString("clientAuthAsk.validityPeriod",
+                        [cert.validity.notBeforeLocalTime,
+                         cert.validity.notAfterLocalTime]),
+    ];
+    let keyUsages = cert.keyUsages;
+    if (keyUsages) {
+      detailLines.push(this.formatString("clientAuthAsk.keyUsages",
+                                         [keyUsages]));
+    }
+    let emailAddresses = cert.getEmailAddresses({});
+    if (emailAddresses.length > 0) {
+      let joinedAddresses = emailAddresses.join(", ");
+      detailLines.push(this.formatString("clientAuthAsk.emailAddresses",
+                                         [joinedAddresses]));
+    }
+    detailLines.push(this.formatString("clientAuthAsk.issuedBy",
+                                       [cert.issuerName]));
+    detailLines.push(this.formatString("clientAuthAsk.storedOn",
+                                       [cert.tokenName]));
+
+    return detailLines.join("<br/>");
+  },
+
   viewCertDetails: function(details) {
     let p = this.getPrompt(this.getString("clientAuthAsk.message3"),
                     '',
                     [ this.getString("nssdialogs.ok.label") ]);
     p.addLabel({ label: details });
     this.showPrompt(p);
   },
 
-  chooseCertificate: function(ctx, cnAndPort, organization, issuerOrg,
-                              certNickList, certDetailsList, count,
+  chooseCertificate: function(ctx, cnAndPort, organization, issuerOrg, certList,
                               selectedIndex) {
     let rememberSetting =
       Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");
 
     let serverRequestedDetails = [
       cnAndPort,
       this.formatString("clientAuthAsk.organization", [organization]),
       this.formatString("clientAuthAsk.issuer", [issuerOrg]),
     ].join("<br/>");
 
+    let certNickList = [];
+    let certDetailsList = [];
+    for (let i = 0; i < certList.length; i++) {
+      let cert = certList.queryElementAt(i, Ci.nsIX509Cert);
+      certNickList.push(this.formatString("clientAuthAsk.nickAndSerial",
+                                          [cert.nickname, cert.serialNumber]));
+      certDetailsList.push(this.getCertDetails(cert));
+    }
+
     selectedIndex.value = 0;
     while (true) {
       let buttons = [
         this.getString("nssdialogs.ok.label"),
         this.getString("clientAuthAsk.viewCert.label"),
         this.getString("nssdialogs.cancel.label"),
       ];
       let prompt = this.getPrompt(this.getString("clientAuthAsk.title"),
--- a/mobile/android/locales/en-US/chrome/pippki.properties
+++ b/mobile/android/locales/en-US/chrome/pippki.properties
@@ -14,22 +14,50 @@ downloadCert.trustEmail=Trust to identif
 downloadCert.trustObjSign=Trust to identify software developers.
 pkcs12.getpassword.title=Password Entry Dialog
 pkcs12.getpassword.message=Please enter the password that was used to encrypt this certificate backup.
 clientAuthAsk.title=User Identification Request
 clientAuthAsk.message1=This site has requested that you identify yourself with a certificate:
 clientAuthAsk.message2=Choose a certificate to present as identification:
 clientAuthAsk.message3=Details of selected certificate:
 clientAuthAsk.remember.label=Remember this decision
+# LOCALIZATION NOTE(clientAuthAsk.nickAndSerial): Represents a single cert when
+# the user is choosing from a list of certificates.
+# %1$S is the nickname of the cert.
+# %2$S is the serial number of the cert in AA:BB:CC hex format.
+clientAuthAsk.nickAndSerial=%1$S [%2$S]
 # LOCALIZATION NOTE(clientAuthAsk.organization): %S is the Organization of the
 # server cert.
 clientAuthAsk.organization=Organization: "%S"
 # LOCALIZATION NOTE(clientAuthAsk.issuer): %S is the Organization of the
 # issuer cert of the server cert.
 clientAuthAsk.issuer=Issued Under: "%S"
+# LOCALIZATION NOTE(clientAuthAsk.issuedTo): %1$S is the Distinguished Name of
+# the currently selected client cert, such as "CN=John Doe,OU=Example" (without
+# quotes).
+clientAuthAsk.issuedTo=Issued to: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.serial): %1$S is the serial number of the
+# selected cert in AA:BB:CC hex format.
+clientAuthAsk.serial=Serial number: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.validityPeriod):
+# %1$S is the already localized notBefore date of the selected cert.
+# %2$S is the already localized notAfter date of the selected cert.
+clientAuthAsk.validityPeriod=Valid from %1$S to %2$S
+# LOCALIZATION NOTE(clientAuthAsk.keyUsages): %1$S is a comma separated list of
+# already localized key usages the selected cert is valid for.
+clientAuthAsk.keyUsages=Key Usages: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.emailAddresses): %1$S is a comma separated
+# list of e-mail addresses the selected cert is valid for.
+clientAuthAsk.emailAddresses=Email addresses: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.issuedBy): %1$S is the Distinguished Name of
+# the cert which issued the selected cert.
+clientAuthAsk.issuedBy=Issued by: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.storedOn): %1$S is the name of the PKCS #11
+# token the selected cert is stored on.
+clientAuthAsk.storedOn=Stored on: %1$S
 clientAuthAsk.viewCert.label=View
 
 certmgr.title=Certificate Details
 # These strings are stolen from security/manager/locales/en-US/chrome/pippki/certManager.dtd
 certmgr.subjectinfo.label=Issued To
 certmgr.issuerinfo.label=Issued By
 certmgr.periodofvalidity.label=Period of Validity
 certmgr.fingerprints.label=Fingerprints
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -48,22 +48,50 @@ certNotVerified_CertNotTrusted=Could not
 certNotVerified_IssuerNotTrusted=Could not verify this certificate because the issuer is not trusted.
 certNotVerified_IssuerUnknown=Could not verify this certificate because the issuer is unknown.
 certNotVerified_CAInvalid=Could not verify this certificate because the CA certificate is invalid.
 certNotVerified_AlgorithmDisabled=Could not verify this certificate because it was signed using a signature algorithm that was disabled because that algorithm is not secure.
 certNotVerified_Unknown=Could not verify this certificate for unknown reasons.
 
 #Client auth
 clientAuthRemember=Remember this decision
+# LOCALIZATION NOTE(clientAuthNickAndSerial): Represents a single cert when the
+# user is choosing from a list of certificates.
+# %1$S is the nickname of the cert.
+# %2$S is the serial number of the cert in AA:BB:CC hex format.
+clientAuthNickAndSerial=%1$S [%2$S]
 # LOCALIZATION NOTE(clientAuthMessage1): %S is the Organization of the server
 # cert.
 clientAuthMessage1=Organization: ā€œ%Sā€
 # LOCALIZATION NOTE(clientAuthMessage2): %S is the Organization of the issuer
 # cert of the server cert.
 clientAuthMessage2=Issued Under: ā€œ%Sā€
+# LOCALIZATION NOTE(clientAuthIssuedTo): %1$S is the Distinguished Name of the
+# currently selected client cert, such as "CN=John Doe,OU=Example" (without
+# quotes).
+clientAuthIssuedTo=Issued to: %1$S
+# LOCALIZATION NOTE(clientAuthSerial): %1$S is the serial number of the selected
+# cert in AA:BB:CC hex format.
+clientAuthSerial=Serial number: %1$S
+# LOCALIZATION NOTE(clientAuthValidityPeriod):
+# %1$S is the already localized notBefore date of the selected cert.
+# %2$S is the already localized notAfter date of the selected cert.
+clientAuthValidityPeriod=Valid from %1$S to %2$S
+# LOCALIZATION NOTE(clientAuthKeyUsages): %1$S is a comma separated list of
+# already localized key usages the selected cert is valid for.
+clientAuthKeyUsages=Key Usages: %1$S
+# LOCALIZATION NOTE(clientAuthEmailAddresses): %1$S is a comma separated list of
+# e-mail addresses the selected cert is valid for.
+clientAuthEmailAddresses=Email addresses: %1$S
+# LOCALIZATION NOTE(clientAuthIssuedBy): %1$S is the Distinguished Name of the
+# cert which issued the selected cert.
+clientAuthIssuedBy=Issued by: %1$S
+# LOCALIZATION NOTE(clientAuthStoredOn): %1$S is the name of the PKCS #11 token
+# the selected cert is stored on.
+clientAuthStoredOn=Stored on: %1$S
 
 #Page Info
 pageInfo_NoEncryption=Connection Not Encrypted
 pageInfo_Privacy_None1=The website %S does not support encryption for the page you are viewing.
 pageInfo_Privacy_None2=Information sent over the Internet without encryption can be seen by other people while it is in transit. 
 pageInfo_Privacy_None4=The page you are viewing was not encrypted before being transmitted over the Internet.
 # LOCALIZATION NOTE (pageInfo_EncryptionWithBitsAndProtocol and pageInfo_BrokenEncryption):
 # %1$S is the name of the encryption standard,
--- a/security/manager/pki/nsNSSDialogs.cpp
+++ b/security/manager/pki/nsNSSDialogs.cpp
@@ -156,66 +156,76 @@ nsNSSDialogs::ConfirmDownloadCACert(nsII
   return rv;
 }
 
 NS_IMETHODIMP
 nsNSSDialogs::ChooseCertificate(nsIInterfaceRequestor* ctx,
                                 const nsAString& cnAndPort,
                                 const nsAString& organization,
                                 const nsAString& issuerOrg,
-                                const char16_t** certNickList,
-                                const char16_t** certDetailsList, uint32_t count,
+                                nsIArray* certList,
                         /*out*/ uint32_t* selectedIndex,
                         /*out*/ bool* certificateChosen)
 {
   NS_ENSURE_ARG_POINTER(ctx);
+  NS_ENSURE_ARG_POINTER(certList);
   NS_ENSURE_ARG_POINTER(selectedIndex);
   NS_ENSURE_ARG_POINTER(certificateChosen);
 
-  nsresult rv;
-  uint32_t i;
-
   *certificateChosen = false;
 
   nsCOMPtr<nsIDialogParamBlock> block =
-           do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
-  if (!block) return NS_ERROR_FAILURE;
+    do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
+  if (!block) {
+    return NS_ERROR_FAILURE;
+  }
 
-  block->SetNumberStrings(4+count*2);
+  nsCOMPtr<nsIMutableArray> paramBlockArray = nsArrayBase::Create();
+  if (!paramBlockArray) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = paramBlockArray->AppendElement(certList, false);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = block->SetObjects(paramBlockArray);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = block->SetNumberStrings(3);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   rv = block->SetString(0, PromiseFlatString(cnAndPort).get());
-  if (NS_FAILED(rv)) return rv;
-
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
   rv = block->SetString(1, PromiseFlatString(organization).get());
-  if (NS_FAILED(rv)) return rv;
-
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
   rv = block->SetString(2, PromiseFlatString(issuerOrg).get());
-  if (NS_FAILED(rv)) return rv;
-
-  for (i = 0; i < count; i++) {
-    rv = block->SetString(i+3, certNickList[i]);
-    if (NS_FAILED(rv)) return rv;
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  for (i = 0; i < count; i++) {
-    rv = block->SetString(i+count+3, certDetailsList[i]);
-    if (NS_FAILED(rv)) return rv;
+  rv = nsNSSDialogHelper::openDialog(nullptr,
+                                     "chrome://pippki/content/clientauthask.xul",
+                                     block);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  rv = block->SetInt(0, count);
-  if (NS_FAILED(rv)) return rv;
-
-  rv = nsNSSDialogHelper::openDialog(nullptr,
-                                "chrome://pippki/content/clientauthask.xul",
-                                block);
-  if (NS_FAILED(rv)) return rv;
-
   int32_t status;
   rv = block->GetInt(0, &status);
-  if (NS_FAILED(rv)) return rv;
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   nsCOMPtr<nsIClientAuthUserDecision> extraResult = do_QueryInterface(ctx);
   if (extraResult) {
     int32_t rememberSelection;
     rv = block->GetInt(2, &rememberSelection);
     if (NS_SUCCEEDED(rv)) {
       extraResult->SetRememberClientAuthCertificate(rememberSelection!=0);
     }
--- a/security/manager/pki/resources/content/clientauthask.js
+++ b/security/manager/pki/resources/content/clientauthask.js
@@ -6,31 +6,40 @@
 /* import-globals-from pippki.js */
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 /**
+ * The pippki <stringbundle> element.
+ * @type <stringbundle>
+ */
+var bundle;
+/**
+ * The array of certs the user can choose from.
+ * @type nsIArray<nsIX509Cert>
+ */
+var certArray;
+/**
  * The param block to get params from and set results on.
  * @type nsIDialogParamBlock
  */
 var dialogParams;
-var itemCount = 0;
 /**
  * The checkbox storing whether the user wants to remember the selected cert.
  * @type nsIDOMXULCheckboxElement
  */
 var rememberBox;
 
 function onLoad() {
   dialogParams = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
 
-  let bundle = document.getElementById("pippki_bundle");
+  bundle = document.getElementById("pippki_bundle");
   let rememberSetting =
     Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");
 
   rememberBox = document.getElementById("rememberBox");
   rememberBox.label = bundle.getString("clientAuthRemember");
   rememberBox.checked = rememberSetting;
 
   let cnAndPort = dialogParams.GetString(0);
@@ -39,38 +48,65 @@ function onLoad() {
   let formattedOrg = bundle.getFormattedString("clientAuthMessage1", [org]);
   let formattedIssuerOrg = bundle.getFormattedString("clientAuthMessage2",
                                                      [issuerOrg]);
   setText("hostname", cnAndPort);
   setText("organization", formattedOrg);
   setText("issuer", formattedIssuerOrg);
 
   let selectElement = document.getElementById("nicknames");
-  itemCount = dialogParams.GetInt(0);
-  for (let i = 0; i < itemCount; i++) {
+  certArray = dialogParams.objects.queryElementAt(0, Ci.nsIArray);
+  for (let i = 0; i < certArray.length; i++) {
     let menuItemNode = document.createElement("menuitem");
-    let nick = dialogParams.GetString(i + 3);
+    let cert = certArray.queryElementAt(i, Ci.nsIX509Cert);
+    let nickAndSerial =
+      bundle.getFormattedString("clientAuthNickAndSerial",
+                                [cert.nickname, cert.serialNumber]);
     menuItemNode.setAttribute("value", i);
-    menuItemNode.setAttribute("label", nick); // this is displayed
+    menuItemNode.setAttribute("label", nickAndSerial); // This is displayed.
     selectElement.firstChild.appendChild(menuItemNode);
     if (i == 0) {
       selectElement.selectedItem = menuItemNode;
     }
   }
 
   setDetails();
 }
 
 /**
  * Populates the details section with information concerning the selected cert.
  */
 function setDetails() {
   let index = parseInt(document.getElementById("nicknames").value);
-  let details = dialogParams.GetString(index + itemCount + 3);
-  document.getElementById("details").value = details;
+  let cert = certArray.queryElementAt(index, Ci.nsIX509Cert);
+
+  let detailLines = [
+    bundle.getFormattedString("clientAuthIssuedTo", [cert.subjectName]),
+    bundle.getFormattedString("clientAuthSerial", [cert.serialNumber]),
+    bundle.getFormattedString("clientAuthValidityPeriod",
+                              [cert.validity.notBeforeLocalTime,
+                               cert.validity.notAfterLocalTime]),
+  ];
+  let keyUsages = cert.keyUsages;
+  if (keyUsages) {
+    detailLines.push(bundle.getFormattedString("clientAuthKeyUsages",
+                                               [keyUsages]));
+  }
+  let emailAddresses = cert.getEmailAddresses({});
+  if (emailAddresses.length > 0) {
+    let joinedAddresses = emailAddresses.join(", ");
+    detailLines.push(bundle.getFormattedString("clientAuthEmailAddresses",
+                                               [joinedAddresses]));
+  }
+  detailLines.push(bundle.getFormattedString("clientAuthIssuedBy",
+                                             [cert.issuerName]));
+  detailLines.push(bundle.getFormattedString("clientAuthStoredOn",
+                                             [cert.tokenName]));
+
+  document.getElementById("details").value = detailLines.join("\n");
 }
 
 function onCertSelected() {
   setDetails();
 }
 
 function doOK() {
   // Signal that the user accepted.
--- a/security/manager/ssl/nsIClientAuthDialogs.idl
+++ b/security/manager/ssl/nsIClientAuthDialogs.idl
@@ -1,39 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface nsIArray;
 interface nsIInterfaceRequestor;
 
 /**
  * Provides UI for SSL client-auth dialogs.
  */
 [scriptable, uuid(fa4c7520-1433-11d5-ba24-00108303b117)]
 interface nsIClientAuthDialogs : nsISupports
 {
   /**
    * Called when a user is asked to choose a certificate for client auth.
    *
    * @param ctx Context that allows at least nsIClientAuthUserDecision to be
    *            queried.
    * @param cnAndPort Common Name of the server cert and the port of the server.
    * @param organization Organization field of the server cert.
    * @param issuerOrg Organization field of the issuer cert of the server cert.
+   * @param certList List of certificates the user can choose from.
+   * @param selectedIndex Index of the cert in |certList| that the user chose.
+   *                      Ignored if the return value is false.
    * @return true if a certificate was chosen. false if the user canceled.
    */
   boolean chooseCertificate(in nsIInterfaceRequestor ctx,
                             in AString cnAndPort,
                             in AString organization,
                             in AString issuerOrg,
-                            [array, size_is(count)] in wstring certNickList,
-                            [array, size_is(count)] in wstring certDetailsList,
-                            in unsigned long count,
+                            in nsIArray certList,
                             out unsigned long selectedIndex);
 };
 
 [scriptable, uuid(95c4373e-bdd4-4a63-b431-f5b000367721)]
 interface nsIClientAuthUserDecision : nsISupports
 {
   attribute boolean rememberClientAuthCertificate;
 };
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -16,16 +16,17 @@
 #include "SharedSSLState.h"
 #include "keyhi.h"
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
+#include "nsArrayUtils.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsClientAuthRemember.h"
 #include "nsContentUtils.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsIConsoleService.h"
 #include "nsIPrefService.h"
 #include "nsISocketProvider.h"
 #include "nsIWebProgressListener.h"
@@ -2094,18 +2095,16 @@ ClientAuthDataRunnable::RunOnTargetThrea
   // We check the value of a pref in this runnable, so this runnable should only
   // be run on the main thread.
   MOZ_ASSERT(NS_IsMainThread());
 
   UniquePLArenaPool arena;
   char** caNameStrings;
   UniqueCERTCertificate cert;
   UniqueSECKEYPrivateKey privKey;
-  UniqueCERTCertNicknames nicknames;
-  int32_t NumberOfCerts = 0;
   void* wincx = mSocketInfo;
   nsresult rv;
 
   nsCOMPtr<nsIX509Cert> socketClientCert;
   mSocketInfo->GetClientCert(getter_AddRefs(socketClientCert));
 
   // If a client cert preference was set on the socket info, use that and skip
   // the client cert UI and/or search of the user's past cert decisions.
@@ -2242,18 +2241,16 @@ ClientAuthDataRunnable::RunOnTargetThrea
           hasRemembered = false;
         }
       }
     }
 
     if (!hasRemembered) {
       // user selects a cert to present
       nsCOMPtr<nsIClientAuthDialogs> dialogs;
-      char16_t** certNicknameList = nullptr;
-      char16_t** certDetailsList = nullptr;
 
       // find all user certs that are for SSL
       // note that we are allowing expired certs in this list
       UniqueCERTCertList certList(
         CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient,
                                   false, false, wincx));
       if (!certList) {
         goto loser;
@@ -2270,31 +2267,16 @@ ClientAuthDataRunnable::RunOnTargetThrea
         }
       }
 
       if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
         // list is empty - no matching certs
         goto loser;
       }
 
-      // filter it further for hostname restriction
-      for (CERTCertListNode* node = CERT_LIST_HEAD(certList.get());
-           !CERT_LIST_END(node, certList.get());
-           node = CERT_LIST_NEXT(node)) {
-        ++NumberOfCerts;
-      }
-
-      nicknames.reset(getNSSCertNicknamesFromCertList(certList));
-
-      if (!nicknames) {
-        goto loser;
-      }
-
-      NS_ASSERTION(nicknames->numnicknames == NumberOfCerts, "nicknames->numnicknames != NumberOfCerts");
-
       // Get CN and O of the subject and O of the issuer
       UniquePORTString ccn(CERT_GetCommonName(&mServerCert->subject));
       NS_ConvertUTF8toUTF16 cn(ccn.get());
 
       int32_t port;
       mSocketInfo->GetPort(&port);
 
       nsAutoString cn_host_port;
@@ -2311,93 +2293,63 @@ ClientAuthDataRunnable::RunOnTargetThrea
       }
 
       UniquePORTString corg(CERT_GetOrgName(&mServerCert->subject));
       NS_ConvertUTF8toUTF16 org(corg.get());
 
       UniquePORTString cissuer(CERT_GetOrgName(&mServerCert->issuer));
       NS_ConvertUTF8toUTF16 issuer(cissuer.get());
 
-      certNicknameList =
-        (char16_t**)moz_xmalloc(sizeof(char16_t*)* nicknames->numnicknames);
-      if (!certNicknameList)
-        goto loser;
-      certDetailsList =
-        (char16_t**)moz_xmalloc(sizeof(char16_t*)* nicknames->numnicknames);
-      if (!certDetailsList) {
-        free(certNicknameList);
+      nsCOMPtr<nsIMutableArray> certArray = nsArrayBase::Create();
+      if (!certArray) {
         goto loser;
       }
 
-      int32_t CertsToUse = 0;
       for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
-           !CERT_LIST_END(node, certList) && CertsToUse < nicknames->numnicknames;
+           !CERT_LIST_END(node, certList);
            node = CERT_LIST_NEXT(node)) {
-        RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
-
-        if (!tempCert)
-          continue;
-
-        NS_ConvertUTF8toUTF16 i_nickname(nicknames->nicknames[CertsToUse]);
-        nsAutoString nickWithSerial, details;
-
-        if (NS_FAILED(tempCert->FormatUIStrings(i_nickname, nickWithSerial, details)))
-          continue;
-
-        certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
-        if (!certNicknameList[CertsToUse])
-          continue;
-        certDetailsList[CertsToUse] = ToNewUnicode(details);
-        if (!certDetailsList[CertsToUse]) {
-          free(certNicknameList[CertsToUse]);
-          continue;
+        nsCOMPtr<nsIX509Cert> tempCert = nsNSSCertificate::Create(node->cert);
+        if (!tempCert) {
+          goto loser;
         }
 
-        ++CertsToUse;
+        rv = certArray->AppendElement(tempCert, false);
+        if (NS_FAILED(rv)) {
+          goto loser;
+        }
       }
 
       // Throw up the client auth dialog and get back the index of the selected cert
       rv = getNSSDialogs(getter_AddRefs(dialogs),
                          NS_GET_IID(nsIClientAuthDialogs),
                          NS_CLIENTAUTHDIALOGS_CONTRACTID);
 
       if (NS_FAILED(rv)) {
-        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
-        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
         goto loser;
       }
 
       uint32_t selectedIndex = 0;
       bool certChosen = false;
       rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port, org, issuer,
-                                      (const char16_t**)certNicknameList,
-                                      (const char16_t**)certDetailsList,
-                                      CertsToUse, &selectedIndex, &certChosen);
-
-      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
-      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
-
+                                      certArray, &selectedIndex, &certChosen);
       if (NS_FAILED(rv)) {
         goto loser;
       }
 
       // even if the user has canceled, we want to remember that, to avoid repeating prompts
       bool wantRemember = false;
       mSocketInfo->GetRememberClientAuthCertificate(&wantRemember);
 
       if (certChosen) {
-        uint32_t i = 0;
-        for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
-             !CERT_LIST_END(node, certList);
-             ++i, node = CERT_LIST_NEXT(node)) {
-          if (i == selectedIndex) {
-            cert.reset(CERT_DupCertificate(node->cert));
-            break;
-          }
+        nsCOMPtr<nsIX509Cert> selectedCert = do_QueryElementAt(certArray,
+                                                               selectedIndex);
+        if (!selectedCert) {
+          goto loser;
         }
+        cert.reset(selectedCert->GetCert());
       }
 
       if (cars && wantRemember) {
         cars->RememberDecision(hostname, mServerCert,
                                certChosen ? cert.get() : nullptr);
       }
     }