Bug 1186286 - Consult more than just the CN when deleting a cert so it's always clear what's being deleted. r=mgoodwin
Deleting certs via the cert manager results in a confirmation dialog popping up.
This dialog contains a list of certs that were selected for deletion.
Currently, only the Common Name of each cert is used to represent it in the list.
This is a problem for certs that don't have a CN. More jarringly, the cert
manager itself falls back to various other attributes, so for example selecting
a cert that is represented by its OU ends up popping up a dialog where an empty
list is shown.
MozReview-Commit-ID: 3z4BebW8Ucd
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -1,24 +1,26 @@
-#
# 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/.
CertPassPrompt=Please enter the Personal Security Password for the PSM Private Keys security device.
-#These are for dialogs
-#Download Cert dialog
+# LOCALIZATION NOTE(certWithSerial): Used for semi-uniquely representing a cert.
+# %1$S is the serial number of the cert in AA:BB:CC hex format.
+certWithSerial=Certificate with serial number: %1$S
+
+# Download Cert dialog
newCAMessage1=Do you want to trust “%S” for the following purposes?
unnamedCA=Certificate Authority (unnamed)
-#For editing cert trust
+# For editing cert trust
editTrustCA=The certificate “%S” represents a Certificate Authority.
-#For Deleting Certificates
+# For Deleting Certificates
deleteSslCertConfirm3=Are you sure you want to delete these server exceptions?
deleteSslCertImpact3=If you delete a server exception, you restore the usual security checks for that server and require it uses a valid certificate.
deleteSslCertTitle3=Delete Server Certificate Exceptions
deleteUserCertConfirm=Are you sure you want to delete these certificates?
deleteUserCertImpact=If you delete one of your own certificates, you can no longer use it to identify yourself.
deleteUserCertTitle=Delete your Certificates
@@ -28,35 +30,34 @@ deleteCaCertTitle2=Delete or Distrust CA
deleteEmailCertConfirm=Are you sure you want to delete these people’s e-mail certificates?
deleteEmailCertImpactDesc=If you delete a person’s e-mail certificate, you will no longer be able to send encrypted e-mail to that person.
deleteEmailCertTitle=Delete E-Mail Certificates
deleteOrphanCertConfirm=Are you sure you want to delete these certificates?
deleteOrphanCertTitle=Delete Certificates
-
-#PKCS#12 file dialogs
+# PKCS#12 file dialogs
chooseP12RestoreFileDialog2=Certificate File to Import
chooseP12BackupFileDialog=File Name to Backup
file_browse_PKCS12_spec=PKCS12 Files
getPKCS12FilePasswordMessage=Please enter the password that was used to encrypt this certificate backup:
-#Cert verification
+# Cert verification
certVerified=This certificate has been verified for the following uses:
certNotVerified_CertRevoked=Could not verify this certificate because it has been revoked.
certNotVerified_CertExpired=Could not verify this certificate because it has expired.
certNotVerified_CertNotTrusted=Could not verify this certificate because it is not trusted.
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
+# 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(clientAuthHostnameAndPort):
# %1$S is the hostname of the server.
@@ -87,38 +88,38 @@ clientAuthKeyUsages=Key Usages: %1$S
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
+# 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,
# %2$S is the key size of the cipher.
# %3$S is protocol version like "SSL 3" or "TLS 1.2"
pageInfo_EncryptionWithBitsAndProtocol=Connection Encrypted (%1$S, %2$S bit keys, %3$S)
pageInfo_BrokenEncryption=Broken Encryption (%1$S, %2$S bit keys, %3$S)
pageInfo_Privacy_Encrypted1=The page you are viewing was encrypted before being transmitted over the Internet.
pageInfo_Privacy_Encrypted2=Encryption makes it difficult for unauthorized people to view information traveling between computers. It is therefore unlikely that anyone read this page as it traveled across the network.
pageInfo_MixedContent=Connection Partially Encrypted
pageInfo_MixedContent2=Parts of the page you are viewing were not encrypted before being transmitted over the Internet.
pageInfo_WeakCipher=Your connection to this website uses weak encryption and is not private. Other people can view your information or modify the website’s behavior.
-#Cert Viewer
+# Cert Viewer
certDetails=Certificate Viewer:
notPresent=<Not Part Of Certificate>
-#Token Manager
+# Token Manager
password_not_set=(not set)
failed_pw_change=Unable to change Master Password.
incorrect_pw=You did not enter the correct current Master Password. Please try again.
pw_change_ok=Master Password successfully changed.
pw_erased_ok=Warning! You have deleted your Master Password.
pw_not_wanted=Warning! You have decided not to use a Master Password.
pw_empty_warning=Your stored web and email passwords, form data, and private keys will not be protected.
pw_change2empty_in_fips_mode=You are currently in FIPS mode. FIPS requires a non-empty Master Password.
@@ -161,17 +162,17 @@ CertFormatPKCS7=X.509 Certificate (PKCS#
CertFormatPKCS7Chain=X.509 Certificate with chain (PKCS#7)
writeFileFailure=File Error
writeFileFailed=Can’t write to file %S:\n%S.
writeFileAccessDenied=Access denied
writeFileIsLocked=File is locked
writeFileNoDeviceSpace=No space left on device
writeFileUnknownError=Unknown error
-#Add Security Exception dialog
+# Add Security Exception dialog
addExceptionBrandedWarning2=You are about to override how %S identifies this site.
addExceptionInvalidHeader=This site attempts to identify itself with invalid information.
addExceptionDomainMismatchShort=Wrong Site
addExceptionDomainMismatchLong2=The certificate belongs to a different site, which could mean that someone is trying to impersonate this site.
addExceptionExpiredShort=Outdated Information
addExceptionExpiredLong2=The certificate is not currently valid. It may have been stolen or lost, and could be used by someone to impersonate this site.
addExceptionUnverifiedOrBadSignatureShort=Unknown Identity
addExceptionUnverifiedOrBadSignatureLong2=The certificate is not trusted because it hasn’t been verified as issued by a trusted authority using a secure signature.
--- a/security/manager/pki/resources/content/certManager.js
+++ b/security/manager/pki/resources/content/certManager.js
@@ -1,14 +1,16 @@
/* 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/. */
/* import-globals-from pippki.js */
"use strict";
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
const nsIFilePicker = Components.interfaces.nsIFilePicker;
const nsFilePicker = "@mozilla.org/filepicker;1";
const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
const nsIX509Cert = Components.interfaces.nsIX509Cert;
const nsICertTree = Components.interfaces.nsICertTree;
const nsCertTree = "@mozilla.org/security/nsCertTree;1";
const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
@@ -404,40 +406,33 @@ function deleteCerts()
return;
}
var params = Components.classes[nsDialogParamBlock].createInstance(nsIDialogParamBlock);
var selTab = document.getElementById('certMgrTabbox').selectedItem;
var selTabID = selTab.getAttribute('id');
- params.SetNumberStrings(numcerts + 1);
-
switch (selTabID) {
case "mine_tab":
case "websites_tab":
case "ca_tab":
case "others_tab":
case "orphan_tab":
params.SetString(0, selTabID);
break;
default:
return;
}
- params.SetInt(0, numcerts);
- for (let t = 0; t < numcerts; t++) {
- let treeItem = selected_tree_items[t];
- let cert = treeItem.cert;
- if (!cert) {
- params.SetString(t + 1, treeItem.hostPort);
- } else {
- params.SetString(t + 1, cert.commonName);
- }
+ let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ for (let treeItem of selected_tree_items) {
+ array.appendElement(treeItem, false);
}
+ params.objects = array;
window.openDialog('chrome://pippki/content/deletecert.xul', "",
'chrome,centerscreen,modal', params);
if (params.GetInt(1) == 1) {
// user closed dialog with OK
var treeView = null;
var loadParam = null;
--- a/security/manager/pki/resources/content/deletecert.js
+++ b/security/manager/pki/resources/content/deletecert.js
@@ -1,33 +1,60 @@
/* 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/. */
/* import-globals-from pippki.js */
"use strict";
-const nsIX509Cert = Components.interfaces.nsIX509Cert;
-const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
-const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
-const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+/**
+ * Param block to get passed in args and to set return values to.
+ * @type nsIDialogParamBlock
+ */
+var gParams;
-var certdb;
-var gParams;
+/**
+ * Returns the most appropriate string to represent the given nsICertTreeItem.
+ * @param {nsICertTreeItem} certTreeItem
+ * The item to represent.
+ * @returns {String}
+ * A representative string.
+ */
+function certTreeItemToString(certTreeItem) {
+ let cert = certTreeItem.cert;
+ if (!cert) {
+ return certTreeItem.hostPort;
+ }
+
+ const attributes = [
+ cert.commonName,
+ cert.organizationalUnit,
+ cert.organization,
+ cert.subjectName,
+ ];
+ for (let attribute of attributes) {
+ if (attribute) {
+ return attribute;
+ }
+ }
+
+ let bundle = document.getElementById("pippki_bundle");
+ return bundle.getFormattedString("certWithSerial", [cert.serialNumber]);
+}
function setWindowName()
{
- gParams = window.arguments[0].QueryInterface(nsIDialogParamBlock);
-
- var typeFlag = gParams.GetString(0);
- var numberOfCerts = gParams.GetInt(0);
+ gParams = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
- var bundle = document.getElementById("pippki_bundle");
- var title;
- var confirm;
- var impact;
+ let typeFlag = gParams.GetString(0);
+ let bundle = document.getElementById("pippki_bundle");
+ let title;
+ let confirm;
+ let impact;
switch (typeFlag) {
case "mine_tab":
title = bundle.getString("deleteUserCertTitle");
confirm = bundle.getString("deleteUserCertConfirm");
impact = bundle.getString("deleteUserCertImpact");
break;
case "websites_tab":
@@ -49,27 +76,26 @@ function setWindowName()
title = bundle.getString("deleteOrphanCertTitle");
confirm = bundle.getString("deleteOrphanCertConfirm");
impact = "";
break;
default:
return;
}
- var confirReference = document.getElementById('confirm');
- var impactReference = document.getElementById('impact');
document.title = title;
setText("confirm", confirm);
let box = document.getElementById("certlist");
- for (let x = 0; x < numberOfCerts; x++) {
- var listItem = document.createElement("richlistitem");
- var label = document.createElement("label");
- label.setAttribute("value", gParams.GetString(x + 1));
+ for (let x = 0; x < gParams.objects.length; x++) {
+ let listItem = document.createElement("richlistitem");
+ let label = document.createElement("label");
+ let certTreeItem = gParams.objects.queryElementAt(x, Ci.nsICertTreeItem);
+ label.setAttribute("value", certTreeItemToString(certTreeItem));
listItem.appendChild(label);
box.appendChild(listItem);
}
setText("impact", impact);
}
function doOK()
--- a/security/manager/ssl/tests/mochitest/browser/browser.ini
+++ b/security/manager/ssl/tests/mochitest/browser/browser.ini
@@ -4,8 +4,9 @@ support-files =
head.js
*.pem
[browser_bug627234_perwindowpb.js]
[browser_certificateManagerLeak.js]
[browser_certViewer.js]
[browser_clientAuth_connection.js]
[browser_clientAuth_ui.js]
+[browser_deleteCert_ui.js]
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
@@ -0,0 +1,226 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests various aspects of the cert delete confirmation dialog.
+// Among other things, tests that for each type of cert that can be deleted:
+// 1. The various lines of explanation text are correctly set.
+// 2. The implementation correctly falls back through multiple cert attributes
+// to determine what to display to represent a cert.
+
+/**
+ * An array of tree items corresponding to TEST_CASES.
+ * @type nsIMutableArray<nsICertTreeItem>
+ */
+var gCertArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+var gImportedCerts = [];
+
+const FAKE_HOST_PORT = "Fake host and port";
+
+/**
+ * @typedef {TestCase}
+ * @type Object
+ * @property {String} certFilename
+ * Filename of the cert, or null if we don't want to import a cert for
+ * this test case (i.e. we expect the hostPort attribute of
+ * nsICertTreeItem to be used).
+ * @property {String} expectedDisplayString
+ * The string we expect the UI to display to represent the given cert.
+ */
+
+/**
+ * A list of test cases representing certs that get "deleted".
+ * @type TestCase[]
+ */
+const TEST_CASES = [
+ { certFilename: null,
+ expectedDisplayString: FAKE_HOST_PORT },
+ { certFilename: "has-cn.pem",
+ expectedDisplayString: "Foo" },
+ { certFilename: "has-ou.pem",
+ expectedDisplayString: "Bar" },
+ { certFilename: "has-o.pem",
+ expectedDisplayString: "Baz" },
+ { certFilename: "has-non-empty-subject.pem",
+ expectedDisplayString: "C=US" },
+ { certFilename: "has-empty-subject.pem",
+ expectedDisplayString: "Certificate with serial number: 0A" },
+];
+
+/**
+ * Opens the cert delete confirmation dialog.
+ *
+ * @param {String} tabID
+ * The ID of the cert category tab the certs to delete belong to.
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * an array consisting of:
+ * 1. The window of the opened dialog.
+ * 2. The nsIDialogParamBlock passed to the dialog.
+ */
+function openDeleteCertConfirmDialog(tabID) {
+ let params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+ params.SetString(0, tabID);
+ params.objects = gCertArray;
+
+ let win = window.openDialog("chrome://pippki/content/deletecert.xul", "", "",
+ params);
+ return new Promise((resolve, reject) => {
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad);
+ resolve([win, params]);
+ });
+ });
+}
+
+registerCleanupFunction(() => {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+ for (let cert of gImportedCerts) {
+ certdb.deleteCertificate(cert);
+ }
+});
+
+add_task(function* setup() {
+ for (let testCase of TEST_CASES) {
+ let cert = null;
+ if (testCase.certFilename) {
+ cert = yield readCertificate(testCase.certFilename, ",,", gImportedCerts);
+ }
+ let certTreeItem = {
+ hostPort: FAKE_HOST_PORT,
+ cert: cert,
+ QueryInterface(iid) {
+ if (iid.equals(Ci.nsICertTreeItem)) {
+ return this;
+ }
+
+ throw new Error(Cr.NS_ERROR_NO_INTERFACE);
+ }
+ };
+ gCertArray.appendElement(certTreeItem, false);
+ }
+});
+
+/**
+ * Test helper for the below test cases.
+ *
+ * @param {String} tabID
+ * ID of the cert category tab the certs to delete belong to.
+ * @param {String} expectedTitle
+ * Title the dialog is expected to have.
+ * @param {String} expectedConfirmMsg
+ * Confirmation message the dialog is expected to show.
+ * @param {String} expectedImpact
+ * Impact the dialog is expected to show.
+ */
+function* testHelper(tabID, expectedTitle, expectedConfirmMsg, expectedImpact) {
+ let [win, params] = yield openDeleteCertConfirmDialog(tabID);
+ let certList = win.document.getElementById("certlist");
+
+ Assert.equal(win.document.title, expectedTitle,
+ `Actual and expected titles should match for ${tabID}`);
+ Assert.equal(win.document.getElementById("confirm").textContent,
+ expectedConfirmMsg,
+ `Actual and expected confirm message should match for ${tabID}`);
+ Assert.equal(win.document.getElementById("impact").textContent,
+ expectedImpact,
+ `Actual and expected impact should match for ${tabID}`);
+
+ Assert.equal(certList.itemCount, TEST_CASES.length,
+ `No. of certs displayed should match for ${tabID}`);
+ for (let i = 0; i < certList.itemCount; i++) {
+ Assert.equal(certList.getItemAtIndex(i).label,
+ TEST_CASES[i].expectedDisplayString,
+ "Actual and expected display string should match for " +
+ `index ${i} for ${tabID}`);
+ }
+
+ yield BrowserTestUtils.closeWindow(win);
+}
+
+// Test deleting certs from the "Your Certificates" tab.
+add_task(function* testDeletePersonalCerts() {
+ const expectedTitle = "Delete your Certificates";
+ const expectedConfirmMsg =
+ "Are you sure you want to delete these certificates?";
+ const expectedImpact =
+ "If you delete one of your own certificates, you can no longer use it to " +
+ "identify yourself.";
+ yield* testHelper("mine_tab", expectedTitle, expectedConfirmMsg,
+ expectedImpact);
+});
+
+// Test deleting certs from the "People" tab.
+add_task(function* testDeleteOtherPeopleCerts() {
+ const expectedTitle = "Delete E-Mail Certificates";
+ // ’ doesn't seem to work when embedded in the following literals, which is
+ // why escape codes are used instead.
+ const expectedConfirmMsg =
+ "Are you sure you want to delete these people\u2019s e-mail certificates?";
+ const expectedImpact =
+ "If you delete a person\u2019s e-mail certificate, you will no longer be " +
+ "able to send encrypted e-mail to that person.";
+ yield* testHelper("others_tab", expectedTitle, expectedConfirmMsg,
+ expectedImpact);
+});
+
+// Test deleting certs from the "Servers" tab.
+add_task(function* testDeleteServerCerts() {
+ const expectedTitle = "Delete Server Certificate Exceptions";
+ const expectedConfirmMsg =
+ "Are you sure you want to delete these server exceptions?";
+ const expectedImpact =
+ "If you delete a server exception, you restore the usual security checks " +
+ "for that server and require it uses a valid certificate.";
+ yield* testHelper("websites_tab", expectedTitle, expectedConfirmMsg,
+ expectedImpact);
+});
+
+// Test deleting certs from the "Authorities" tab.
+add_task(function* testDeleteCACerts() {
+ const expectedTitle = "Delete or Distrust CA Certificates";
+ const expectedConfirmMsg =
+ "You have requested to delete these CA certificates. For built-in " +
+ "certificates all trust will be removed, which has the same effect. Are " +
+ "you sure you want to delete or distrust?";
+ const expectedImpact =
+ "If you delete or distrust a certificate authority (CA) certificate, " +
+ "this application will no longer trust any certificates issued by that CA.";
+ yield* testHelper("ca_tab", expectedTitle, expectedConfirmMsg,
+ expectedImpact);
+});
+
+// Test deleting certs from the "Other" tab.
+add_task(function* testDeleteOtherCerts() {
+ const expectedTitle = "Delete Certificates";
+ const expectedConfirmMsg =
+ "Are you sure you want to delete these certificates?";
+ const expectedImpact = "";
+ yield* testHelper("orphan_tab", expectedTitle, expectedConfirmMsg,
+ expectedImpact);
+});
+
+// Test that the right values are returned when the dialog is accepted.
+add_task(function* testAcceptDialogReturnValues() {
+ let [win, params] = yield openDeleteCertConfirmDialog("ca_tab" /*arbitrary*/);
+ info("Accepting dialog");
+ win.document.getElementById("deleteCertificate").acceptDialog();
+ yield BrowserTestUtils.windowClosed(win);
+
+ Assert.equal(params.GetInt(1), 1,
+ "1 should be returned to signal user accepted");
+});
+
+// Test that the right values are returned when the dialog is canceled.
+add_task(function* testCancelDialogReturnValues() {
+ let [win, params] = yield openDeleteCertConfirmDialog("ca_tab" /*arbitrary*/);
+ info("Canceling dialog");
+ win.document.getElementById("deleteCertificate").cancelDialog();
+ yield BrowserTestUtils.windowClosed(win);
+
+ Assert.equal(params.GetInt(1), 0,
+ "0 should be returned to signal user canceled");
+});
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-cn.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0DCCAbqgAwIBAgIUBdYRNGqgoPy8QL2qxNIAIbGiUsowCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMDcxDDAKBgNVBAMMA0ZvbzEMMAoGA1UECwwDQmFyMQwwCgYDVQQKDANCYXox
+CzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohR
+qESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+Kv
+WnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+
+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPv
+JxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5
+Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6
+clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOCAQEAB1QaKkpRAhxoeSa6
+vfhiJS9a3kC+ChE/0zRqu/wVaNkxlmnq6rR5e0iZ7K2Xo7FDPxfC14UolaTFs7KH
+bEA4XzCvctDD7J1MTtcGZKDjH8ua4unpm8Ux6cnXqy3SpHzirshxU/cxOm8JtjmI
+89Xq1BQ34fck+wNkK894d3+uniMyy4WrAiVeQHWQ6cUnzqt+8THgWXoRQSvMgEcM
+ItSIylwll9pILylS5p7wI1upXkZD0V72WR+Pp/XSP07MOR13MPkAjZxXP/8hGQRR
+URtLhwGrVf5Ovc9JnFhv3CqkKR/sEcGooA3VcCafVnxyucj1bmXmbkgWCE+b/zMQ
+cVSPvw==
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-cn.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/CN=Foo/OU=Bar/O=Baz/C=US
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIIChjCCAXCgAwIBAgIBCjALBgkqhkiG9w0BAQswDTELMAkGA1UEAwwCY2EwIhgP
+MjAxNDExMjcwMDAwMDBaGA8yMDE3MDIwNDAwMDAwMFowADCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATALBgkqhkiG
+9w0BAQsDggEBAKAFuEA9TJ+o8myZ0AciYyCjsIcQhfPvL16vfhVmbr8IXudjeIQm
+orBPLWOZWT/IVeLOYU+GecS86IFWdBjKLOMoPkZC3o6nQ95txGQrODN1uMQOQBc3
+0AMQFnKbGCgoN8VtAsn7zppipvc6FcG83fh8YnrtGa8r9LBd5r2lX3npvG8qmhWD
+J+2heHQp8D3NfCzad+jQy4hUhHalbH5aTky3T/1UGfgr/cXsDAcZb09Wd5tzDVpS
+CP6iZyumHdH24ta5yYTHEEKDtR+/YU987crD79DcBK+7K2k4feFEzLfPDzmbtM3G
+ZadO9SSf9GWHUovurJJNJaIBMYpNGeq4nT4=
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:
+serialNumber:10
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICpjCCAZCgAwIBAgIUS5/RALAav3GqFmr/hG5FumIUs/gwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMA0xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOCAQEArANWYwZp
+g5Z9sRrvPIaEjgJO4fnTf5/3nECTFljJH7YjGzFhnEGk52aRGubowhTOxAtqK1lc
+cA5QRMuQ/Qrr7xZOdGfoxoMnQXONcEQNobcWbxa0Z8VZrpanAhXevlk5Z/8it96g
+uLtCmgz32L6J2FpsftHI38+IAGjbgZjyOnyr4jGwgltZUKV1YRGClnMQnI6N/8TH
+UrESziJ6JmVWnIwvKWvtwy68gRLvSaJ+lD1KTrqOVris0yPcR+dWlXaHovegxwDB
+gkhtFOYJePK8p7op+WAyKlM922UYBC4X2h/4QeySrC2LYauGFz3ZARO2ylCJ1CsJ
+ypuqIXstbO0h3g==
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/C=US
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-o.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZ6gAwIBAgIUG62RAQRi0nQBf5QEVbVQ6tS4QOwwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMBsxDDAKBgNVBAoMA0JhejELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwCwYJKoZIhvcN
+AQELA4IBAQBKExQp1zdXXkl60yJsRcUNBPbprmHDlYLFtAcAY7s8+IKn+6kWUgNG
+1wrKsaoSJUxK8MO6ULSbK8Xb9djjZ5Xcy622cOe3wk3/AIwc8nhAE6d5mtCDzOGR
+MWEn+wGsCtW+eWDTlefx/+D3zBNAsb7hyW54JKDjfRADN6VtFO4ca4Ybw92G6CRf
+g452KvRl90oZ2cxXyxOP3qdMOpB5a9DkUVS39n7iWgv//f3qlo3eUlHHoeygJQOM
+qca5cnQbYKhat2edgK5OY4eHCFOWXpxRy83/zNywLki4zozwXnTWSmuBpe2jZEev
+nOCxqVD3xceDzZ3NNkFj+c7Q2IX0ykmf
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-o.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/O=Baz/C=US
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-ou.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaygAwIBAgIUI7neyiHwgLifhsVz4PP5NKkCWqIwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMCkxDDAKBgNVBAsMA0JhcjEMMAoGA1UECgwDQmF6MQswCQYDVQQGEwJVUzCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAATALBgkqhkiG9w0BAQsDggEBAGKpDaMHux0AWDSH/3scV6W+6ZFtsAH/kTw2
+zPfPad1z7xECUDzNaLjJYdTiXplBFoB9lFNmM2pb/Z98FZPLCK7wD36jLtSrBjLm
+KlDKieE3aoHHe2RANz3fEod7jV/YVuZXLNQaMEMXAdXV1qfrMOuyiYgo+Crr/EMO
+ApAQvOPxOR34Z7mBPnxVwn3w85zxrpcRzbg+UFJcr1yk0yKrlawSCB1CV/itIkCJ
+sdpqub2wRy6w3nCwaCmp92bmDAOS9K4UG4tCvwKFihYLBx1MiVmUZC90nrXSynh8
+b1hdzFnKZxzoZgM7yNuqw06Xzx0eg8yqi4wLjj+uA3UdZHP7MAk=
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-ou.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/OU=Bar/O=Baz/C=US
--- a/security/manager/ssl/tests/mochitest/browser/moz.build
+++ b/security/manager/ssl/tests/mochitest/browser/moz.build
@@ -13,16 +13,21 @@ BROWSER_CHROME_MANIFESTS += ['browser.in
# when they're automatically generated.)
#test_certificates = (
# 'ca.pem',
# 'code-ee.pem',
# 'ee-from-expired-ca.pem',
# 'ee-from-untrusted-ca.pem',
# 'email-ee.pem',
# 'expired-ca.pem',
+# 'has-cn.pem',
+# 'has-empty-subject.pem',
+# 'has-non-empty-subject.pem',
+# 'has-o.pem',
+# 'has-ou.pem',
# 'invalid.pem',
# 'md5-ee.pem',
# 'revoked.pem',
# 'ssl-ee.pem',
# 'unknown-issuer.pem',
# 'untrusted-ca.pem',
#)
#