author | David Keeler <dkeeler@mozilla.com> |
Wed, 02 May 2018 10:22:58 -0700 | |
changeset 417486 | 3c65a2197eadf11fd6f3a87caf7682cc89558cbd |
parent 417485 | 3029b0beea0eb25cb9b81de89aed56f1f95a0b89 |
child 417487 | 35e7f3b5f06cf96a3738eaeba1cffa02eea36fc4 |
push id | 33968 |
push user | ebalazs@mozilla.com |
push date | Wed, 09 May 2018 09:32:53 +0000 |
treeherder | mozilla-central@a2eccfbeb0ae [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | fkiefer |
bugs | 686149 |
milestone | 62.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/security/manager/pki/resources/content/pippki.js +++ b/security/manager/pki/resources/content/pippki.js @@ -40,24 +40,23 @@ function getDERString(cert) { var derArray = cert.getRawDER(length); var derString = ""; for (var i = 0; i < derArray.length; i++) { derString += String.fromCharCode(derArray[i]); } return derString; } -function getPKCS7String(cert, chainMode) { - var length = {}; - var pkcs7Array = cert.exportAsCMS(chainMode, length); - var pkcs7String = ""; - for (var i = 0; i < pkcs7Array.length; i++) { - pkcs7String += String.fromCharCode(pkcs7Array[i]); +function getPKCS7String(certArray) { + let certList = Cc["@mozilla.org/security/x509certlist;1"] + .createInstance(Ci.nsIX509CertList); + for (let cert of certArray) { + certList.addCert(cert); } - return pkcs7String; + return certList.asPKCS7Blob(); } function getPEMString(cert) { var derb64 = btoa(getDERString(cert)); // Wrap the Base64 string into lines of 64 characters with CRLF line breaks // (as specified in RFC 1421). var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n"); return "-----BEGIN CERTIFICATE-----\r\n" @@ -142,20 +141,20 @@ async function exportToFile(parent, cert for (let i = 1; i < chain.length; i++) { content += getPEMString(chain[i]); } break; case 2: content = getDERString(cert); break; case 3: - content = getPKCS7String(cert, Ci.nsIX509Cert.CMS_CHAIN_MODE_CertOnly); + content = getPKCS7String([cert]); break; case 4: - content = getPKCS7String(cert, Ci.nsIX509Cert.CMS_CHAIN_MODE_CertChainWithRoot); + content = getPKCS7String(chain); break; case 0: default: content = getPEMString(cert); break; } var msg; var written = 0;
--- a/security/manager/ssl/nsIX509Cert.idl +++ b/security/manager/ssl/nsIX509Cert.idl @@ -171,23 +171,16 @@ interface nsIX509Cert : nsISupports { /** * True if the certificate is self-signed. CA issued * certificates are always self-signed. */ [must_use] readonly attribute boolean isSelfSigned; /** - * Constants for specifying the chain mode when exporting a certificate - */ - const unsigned long CMS_CHAIN_MODE_CertOnly = 1; - const unsigned long CMS_CHAIN_MODE_CertChain = 2; - const unsigned long CMS_CHAIN_MODE_CertChainWithRoot = 3; - - /** * A comma separated list of localized strings representing the contents of * the certificate's key usage extension, if present. The empty string if the * certificate doesn't have the key usage extension, or has an empty extension. */ [must_use] readonly attribute AString keyUsages; /** @@ -221,30 +214,16 @@ interface nsIX509Cert : nsISupports { /** * The base64 encoding of the DER encoded public key info using the specified * digest. */ [must_use] readonly attribute ACString sha256SubjectPublicKeyInfoDigest; /** - * Obtain the certificate wrapped in a PKCS#7 SignedData structure, - * with or without the certificate chain - * - * @param chainMode Whether to include the chain (with or without the root), - see CMS_CHAIN_MODE constants. - * @param length The number of bytes of the PKCS#7 data. - * @param data The bytes representing the PKCS#7 wrapped certificate. - */ - [must_use] - void exportAsCMS(in unsigned long chainMode, - out unsigned long length, - [retval, array, size_is(length)] out octet data); - - /** * Retrieves the NSS certificate object wrapped by this interface */ [notxpcom, noscript, must_use] CERTCertificatePtr getCert(); /** * Either delete the certificate from all cert databases, * or mark it as untrusted.
--- a/security/manager/ssl/nsIX509CertList.idl +++ b/security/manager/ssl/nsIX509CertList.idl @@ -39,16 +39,25 @@ interface nsIX509CertList : nsISupports [must_use] boolean equals(in nsIX509CertList other); /** * Retrieves the PSM helper class that wraps the NSS certificate list */ [notxpcom, noscript, must_use] nsNSSCertListPtr getCertList(); + + /** + * Encode the list of certificates as a PKCS#7 SignedData structure. No data + * is actually signed - this is merely a way of exporting a collection of + * certificates. + */ + [must_use] + ACString asPKCS7Blob(); + }; %{C++ #define NS_X509CERTLIST_CID { /* 959fb165-6517-487f-ab9b-d8913be53197 */ \ 0x959fb165, \ 0x6517, \ 0x487f, \
--- a/security/manager/ssl/nsNSSCertificate.cpp +++ b/security/manager/ssl/nsNSSCertificate.cpp @@ -750,142 +750,16 @@ nsNSSCertificate::GetRawDER(uint32_t* aL *aLength = mCert->derCert.len; return NS_OK; } } *aLength = 0; return NS_ERROR_FAILURE; } -NS_IMETHODIMP -nsNSSCertificate::ExportAsCMS(uint32_t chainMode, - uint32_t* aLength, uint8_t** aArray) -{ - NS_ENSURE_ARG(aLength); - NS_ENSURE_ARG(aArray); - - nsresult rv = BlockUntilLoadableRootsLoaded(); - if (NS_FAILED(rv)) { - return rv; - } - - if (!mCert) - return NS_ERROR_FAILURE; - - switch (chainMode) { - case nsIX509Cert::CMS_CHAIN_MODE_CertOnly: - case nsIX509Cert::CMS_CHAIN_MODE_CertChain: - case nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot: - break; - default: - return NS_ERROR_INVALID_ARG; - } - - UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr)); - if (!cmsg) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - can't create CMS message\n")); - return NS_ERROR_OUT_OF_MEMORY; - } - - // first, create SignedData with the certificate only (no chain) - UniqueNSSCMSSignedData sigd( - NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), mCert.get(), false)); - if (!sigd) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - can't create SignedData\n")); - return NS_ERROR_FAILURE; - } - - // Calling NSS_CMSSignedData_CreateCertsOnly() will not allow us - // to specify the inclusion of the root, but CERT_CertChainFromCert() does. - // Since CERT_CertChainFromCert() also includes the certificate itself, - // we have to start at the issuing cert (to avoid duplicate certs - // in the SignedData). - if (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChain || - chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot) { - UniqueCERTCertificate issuerCert( - CERT_FindCertIssuer(mCert.get(), PR_Now(), certUsageAnyCA)); - // the issuerCert of a self signed root is the cert itself, - // so make sure we're not adding duplicates, again - if (issuerCert && issuerCert != mCert) { - bool includeRoot = - (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot); - UniqueCERTCertificateList certChain( - CERT_CertChainFromCert(issuerCert.get(), certUsageAnyCA, includeRoot)); - if (certChain) { - if (NSS_CMSSignedData_AddCertList(sigd.get(), certChain.get()) - == SECSuccess) { - Unused << certChain.release(); - } - else { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - can't add chain\n")); - return NS_ERROR_FAILURE; - } - } - else { - // try to add the issuerCert, at least - if (NSS_CMSSignedData_AddCertificate(sigd.get(), issuerCert.get()) - == SECSuccess) { - Unused << issuerCert.release(); - } - else { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - can't add issuer cert\n")); - return NS_ERROR_FAILURE; - } - } - } - } - - NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get()); - if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) - == SECSuccess) { - Unused << sigd.release(); - } - else { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - can't attach SignedData\n")); - return NS_ERROR_FAILURE; - } - - UniquePLArenaPool arena(PORT_NewArena(1024)); - if (!arena) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - out of memory\n")); - return NS_ERROR_OUT_OF_MEMORY; - } - - SECItem certP7 = { siBuffer, nullptr, 0 }; - NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg.get(), nullptr, nullptr, - &certP7, arena.get(), nullptr, - nullptr, nullptr, nullptr, - nullptr, nullptr); - if (!ecx) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - can't create encoder context\n")); - return NS_ERROR_FAILURE; - } - - if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) { - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, - ("nsNSSCertificate::ExportAsCMS - failed to add encoded data\n")); - return NS_ERROR_FAILURE; - } - - *aArray = (uint8_t*)moz_xmalloc(certP7.len); - if (!*aArray) - return NS_ERROR_OUT_OF_MEMORY; - - memcpy(*aArray, certP7.data, certP7.len); - *aLength = certP7.len; - return NS_OK; -} - CERTCertificate* nsNSSCertificate::GetCert() { return (mCert) ? CERT_DupCertificate(mCert.get()) : nullptr; } NS_IMETHODIMP nsNSSCertificate::GetValidity(nsIX509CertValidity** aValidity) @@ -1042,16 +916,96 @@ nsNSSCertList::DupCertList(const UniqueC CERTCertList* nsNSSCertList::GetRawCertList() { return mCertList.get(); } NS_IMETHODIMP +nsNSSCertList::AsPKCS7Blob(/*out*/ nsACString& result) +{ + MOZ_ASSERT(mCertList); + if (!mCertList) { + return NS_ERROR_FAILURE; + } + + UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr)); + if (!cmsg) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - can't create CMS message")); + return NS_ERROR_OUT_OF_MEMORY; + } + + UniqueNSSCMSSignedData sigd(nullptr); + nsresult rv = ForEachCertificateInChain( + [&cmsg, &sigd] (nsCOMPtr<nsIX509Cert> aCert, bool /*unused*/, + /*out*/ bool& /*unused*/) { + // We need an owning handle when calling nsIX509Cert::GetCert(). + UniqueCERTCertificate nssCert(aCert->GetCert()); + if (!sigd) { + sigd.reset(NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), + false)); + if (!sigd) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - can't create SignedData")); + return NS_ERROR_FAILURE; + } + } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) + != SECSuccess) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - can't add cert")); + return NS_ERROR_FAILURE; + } + return NS_OK; + }); + if (NS_FAILED(rv)) { + return rv; + } + + NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get()); + if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) + != SECSuccess) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - can't attach SignedData")); + return NS_ERROR_FAILURE; + } + // cmsg owns sigd now. + Unused << sigd.release(); + + UniquePLArenaPool arena(PORT_NewArena(1024)); + if (!arena) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - out of memory")); + return NS_ERROR_OUT_OF_MEMORY; + } + + SECItem certP7 = { siBuffer, nullptr, 0 }; + NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg.get(), nullptr, nullptr, + &certP7, arena.get(), nullptr, + nullptr, nullptr, nullptr, + nullptr, nullptr); + if (!ecx) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - can't create encoder")); + return NS_ERROR_FAILURE; + } + + if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("nsNSSCertList::AsPKCS7Blob - failed to add encoded data")); + return NS_ERROR_FAILURE; + } + + result.Assign(nsDependentCSubstring(reinterpret_cast<const char*>(certP7.data), + certP7.len)); + return NS_OK; +} + +NS_IMETHODIMP nsNSSCertList::Write(nsIObjectOutputStream* aStream) { NS_ENSURE_STATE(mCertList); nsresult rv = NS_OK; // First, enumerate the certs to get the length of the list uint32_t certListLen = 0; CERTCertListNode* node = nullptr;
--- a/security/manager/ssl/tests/unit/head_psm.js +++ b/security/manager/ssl/tests/unit/head_psm.js @@ -118,21 +118,21 @@ const NO_FLAGS = 0; // with no newlines or BEGIN/END headers. This is a helper function to convert // PEM to the format that nsIX509CertDB requires. function pemToBase64(pem) { return pem.replace(/-----BEGIN CERTIFICATE-----/, "") .replace(/-----END CERTIFICATE-----/, "") .replace(/[\r\n]/g, ""); } -function build_cert_chain(certNames) { +function build_cert_chain(certNames, testDirectory = "bad_certs") { let certList = Cc["@mozilla.org/security/x509certlist;1"] .createInstance(Ci.nsIX509CertList); certNames.forEach(function(certName) { - let cert = constructCertFromFile("bad_certs/" + certName + ".pem"); + let cert = constructCertFromFile(`${testDirectory}/${certName}.pem`); certList.addCert(cert); }); return certList; } function readFile(file) { let fstream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream);
--- a/security/manager/ssl/tests/unit/test_cert_chains.js +++ b/security/manager/ssl/tests/unit/test_cert_chains.js @@ -32,16 +32,150 @@ function test_cert_list_serialization() // Deserialize from the string and compare to the original object let deserialized = serHelper.deserializeObject(serialized); deserialized.QueryInterface(Ci.nsIX509CertList); ok(certList.equals(deserialized), "Deserialized cert list should equal the original"); } +function test_cert_pkcs7_export() { + // This was generated by running BadCertServer locally on the bad_certs + // directory and visiting: + // https://good.include-subdomains.pinning.example.com:8443/ + // and then viewing the certificate chain presented (in the page info dialog) + // and exporting it. + // (NB: test-ca must be imported and trusted for the connection to succeed) + const expectedPKCS7ForDefaultEE = + "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCBmQwggLTMIIBu6ADAgE" + + "CAhRdBTvvC7swO3cbVWIGn/56DrQ+cjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZX" + + "N0IENBMCIYDzIwMTYxMTI3MDAwMDAwWhgPMjAxOTAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB" + + "1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI" + + "BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xz" + + "VJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCy" + + "uwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW" + + "7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE" + + "LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8" + + "wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCDjewR53YLc3HzZKugRDbQVxjJNI" + + "LW6fSIyW9dSglYcWh6aiOK9cZFVtzRWYEYkIlicAyTiPw34bXzxU1cK6sCSmBR+UTXbRPGb" + + "4OOy3MRaoF1m3jxwnPkQwxezDiqJTydCbYcBu0sKwURAZOd5QK922MsOsnrLjNlpRDmuH0V" + + "Fhb5uN2I5mM3NvMnP2Or19O1Bk//iGD6AyJfiZFcii+FsDrJhbzw6lakEV7O/EnD0kk2l7I" + + "0VMtg1xZBbEw7P6+V9zz5cAzaaq7EB0mCE+jJckSzSETBN+7lyVD8gwmHYxxZfPnUM/yvPb" + + "MU9L3xWD/z6HHwO6r+9m7BT+2pHjBCMIIDiTCCAnGgAwIBAgIUWbWLTwLBvfwcoiU7I8lDz" + + "9snfUgwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE2MTEyNzAw" + + "MDAwMFoYDzIwMTkwMjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggE" + + "iMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNU" + + "q07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0" + + "DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ" + + "sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJH" + + "dtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFz" + + "G4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcowgccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob" + + "3N0gg0qLmV4YW1wbGUuY29tghUqLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1z" + + "dWJkb21haW5zLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnB" + + "pbm5pbmcuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi" + + "8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQBE+6IPJK5OeonoQPC4CCWMd" + + "69SjhwS7X6TNgxDJzW7qpVm4SFyYZ2xqzr2zib5LsYek6/jok5LPSpJVeFuSeiesvGMxk0O" + + "4ZEihPxSM4uR4xpCnPzz7LoFIzMELJv5i+cgLw4+6cINPkLjoCUdb+AXSTur7THJaO75B44" + + "I2JjJfMfzgW1FwoWgXL/PQWRw+VY6OY1glqZOXzP+vfSja1SoggpiCzdPx7h1/SEEZov7zh" + + "CZXv1Cenx1njlpcj9wWEJMsyZczMNtiz5GkRrLaqCz9F8ah3NvkvPAZ0oOqtxuQgMXK/c0O" + + "XJVKi0SCJsWqZDoZhCrS/dE9guxlseZqhSIMQAAAAAAAAA="; + let certListDefaultEE = build_cert_chain(["default-ee", "test-ca"]); + let pkcs7DefaultEE = certListDefaultEE.asPKCS7Blob(); + equal(btoa(pkcs7DefaultEE), expectedPKCS7ForDefaultEE, + "PKCS7 export should work as expected for default-ee chain"); + + // This was generated by running BadCertServer locally on the bad_certs + // directory and visiting: + // https://unknownissuer.example.com:8443/ + // and then viewing the certificate presented (in the add certificate + // exception dialog) and exporting it. + const expectedPKCS7ForUnknownIssuer = + "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCA60wggOpMIICkaADAgE" + + "CAhQN8MG4SddxqOKNAeWywCf4fPyU0jANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtUZX" + + "N0IEludGVybWVkaWF0ZSB0byBkZWxldGUwIhgPMjAxNjExMjcwMDAwMDBaGA8yMDE5MDIwN" + + "TAwMDAwMFowLjEsMCoGA1UEAwwjVGVzdCBFbmQtZW50aXR5IGZyb20gdW5rbm93biBpc3N1" + + "ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTw" + + "T2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs" + + "1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkf" + + "bmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA" + + "dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/" + + "l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcIwgb8wgYgGA1UdEQSBgDB+ghl1bm" + + "tub3duaXNzdWVyLmV4YW1wbGUuY29tgjR1bmtub3duaXNzdWVyLmluY2x1ZGUtc3ViZG9tY" + + "Wlucy5waW5uaW5nLmV4YW1wbGUuY29tgit1bmtub3duaXNzdWVyLnRlc3QtbW9kZS5waW5u" + + "aW5nLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2x" + + "vY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAW4Vm4ekBw7QmfxOzy6HsXN8SFh" + + "pLpE0AD1tl0fHR8V30rM8IhrnCi6KzAWceIMY4mesE+hGSkNLArNuRCI6NtGO5TyEGHhNdr" + + "D8CDQ+/w9by5TJIw/ZsWi2xgFRHsrmphQ8e833FEdBuhthOXwaXiVCn9/Ug6OpgsSWJp9T7" + + "AAiwQy2L2MseyfS9kcV/W/9kHiT4NoIz/3hVeLBiTg8silFGaEB02yLomp1T+/rkpqEhNFI" + + "JhZXCa1fL8u3Z/TN40nd756fPfHXwAVuyc5qkr5R1ulVbLKXCoJLzNicfSogsmzveNrhIv2" + + "LP6G2P9YwjmJXM6pXlxFpsWPoyi/Z4LjEAAAAAAAAA"; + let certListUnknownIssuer = build_cert_chain(["unknownissuer"]); + let pkcs7UnknownIssuer = certListUnknownIssuer.asPKCS7Blob(); + equal(btoa(pkcs7UnknownIssuer), expectedPKCS7ForUnknownIssuer, + "PKCS7 export should work as expected for unknown issuer"); + + // This was generated by running OCSPStaplingServer locally on the ocsp_certs + // directory and visiting: + // https://ocsp-stapling-with-intermediate.example.com:8443/ + // and then viewing the certificate chain presented (in the page info dialog) + // and exporting it. + // (NB: test-ca must be imported and trusted for the connection to succeed) + const expectedPKCS7WithIntermediate = + "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"; + let certListWithIntermediate = build_cert_chain(["ocspEEWithIntermediate", + "test-int", "test-ca"], + "ocsp_certs"); + let pkcs7WithIntermediate = certListWithIntermediate.asPKCS7Blob(); + equal(btoa(pkcs7WithIntermediate), expectedPKCS7WithIntermediate, + "PKCS7 export should work as expected for chain with intermediate"); +} + function test_security_info_serialization(securityInfo, expectedErrorCode) { // Serialize the securityInfo to a string let serHelper = Cc["@mozilla.org/network/serialization-helper;1"] .getService(Ci.nsISerializationHelper); let serialized = serHelper.serializeToString(securityInfo); // Deserialize from the string and compare to the original object let deserialized = serHelper.deserializeObject(serialized); @@ -67,16 +201,21 @@ function run_test() { }); // Test serialization of nsIX509CertList add_test(function() { test_cert_list_serialization(); run_next_test(); }); + add_test(function() { + test_cert_pkcs7_export(); + run_next_test(); + }); + // Test successful connection (failedCertChain should be null) add_connection_test( // re-use pinning certs (keeler) "good.include-subdomains.pinning.example.com", PRErrorCodeSuccess, null, function withSecurityInfo(aTransportSecurityInfo) { aTransportSecurityInfo.QueryInterface(Ci.nsITransportSecurityInfo); test_security_info_serialization(aTransportSecurityInfo, 0); equal(aTransportSecurityInfo.failedCertChain, null,