Bug 1612587 - (2/2) incorporate all known potential issuing certificates when filtering client certificates r=kjacobs,jcj

When a server requests a client certificate, it can include a list of
distinguished names that it considers valid issuers for client certificates
(either as direct issuers or as transitive issuers). Before this patch, the
platform would call CERT_FilterCertListByCANames to filter potential client
certificates by this list of names. This function uses the "classic" NSS
certificate path-building algorithm and thus can't make use of other
certificates that gecko may know about, such as third-party intermediates and
preloaded intermediates.

This patch implements client certificate filtering by re-using the path building
implementation provided by mozilla::pkix to determine if each certificate has an
issuer with a name included in the acceptable list. These issuers include
third-party intermediates, preloaded intermediates, and all certificates known
to NSS. Note that this implementation does not actually verify the client
certificates - no signatures are checked and no particular key usages are
enforced. However, some properties are enforced, such as validity periods.

Differential Revision: https://phabricator.services.mozilla.com/D68101

--HG--
rename : security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem.certspec => security/manager/ssl/tests/mochitest/browser/intermediate.pem.certspec
extra : moz-landing-system : lando
This commit is contained in:
Dana Keeler 2020-04-14 02:26:16 +00:00
parent 7f9a18765c
commit 90d81515f7
14 changed files with 431 additions and 66 deletions

View File

@ -57,6 +57,14 @@ interface nsINSSComponent : nsISupports {
*/
Array<Array<octet> > getEnterpriseIntermediates();
/**
* Test utility for adding an intermediate certificate to the current set of
* imported enterprise intermediates, if any. Additions to the set made using
* this function will be cleared when the value of the preference
* "security.enterprise_roots.enabled" changes.
*/
void addEnterpriseIntermediate(in Array<octet> intermediateBytes);
/**
* For performance reasons, the builtin roots module is loaded on a background
* thread. When any code that depends on the builtin roots module runs, it

View File

@ -601,6 +601,31 @@ nsNSSComponent::GetEnterpriseIntermediates(
return CommonGetEnterpriseCerts(enterpriseIntermediates, false);
}
NS_IMETHODIMP
nsNSSComponent::AddEnterpriseIntermediate(
const nsTArray<uint8_t>& intermediateBytes) {
nsresult rv = BlockUntilLoadableCertsLoaded();
if (NS_FAILED(rv)) {
return rv;
}
EnterpriseCert intermediate;
rv = intermediate.Init(intermediateBytes.Elements(),
intermediateBytes.Length(), false);
if (NS_FAILED(rv)) {
return rv;
}
{
MutexAutoLock nsNSSComponentLock(mMutex);
if (!mEnterpriseCerts.append(std::move(intermediate))) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
UpdateCertVerifierWithEnterpriseRoots();
return NS_OK;
}
class LoadLoadableCertsTask final : public Runnable {
public:
LoadLoadableCertsTask(nsNSSComponent* nssComponent,

View File

@ -16,6 +16,9 @@
#include "SSLServerCertVerification.h"
#include "ScopedNSSTypes.h"
#include "SharedSSLState.h"
#ifdef MOZ_NEW_CERT_STORAGE
# include "cert_storage/src/cert_storage.h"
#endif
#include "keyhi.h"
#include "mozilla/Base64.h"
#include "mozilla/Casting.h"
@ -26,6 +29,7 @@
#include "mozilla/Telemetry.h"
#include "mozpkix/pkixnss.h"
#include "mozpkix/pkixtypes.h"
#include "mozpkix/pkixutil.h"
#include "nsArray.h"
#include "nsArrayUtils.h"
#include "nsCRT.h"
@ -49,7 +53,6 @@
#include "sslexp.h"
#include "sslproto.h"
using namespace mozilla;
using namespace mozilla::psm;
//#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
@ -1728,10 +1731,10 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
public:
ClientAuthDataRunnable(nsNSSSocketInfo* info,
const UniqueCERTCertificate& serverCert,
nsTArray<nsCString>& caNamesStrings)
nsTArray<nsTArray<uint8_t>>& collectedCANames)
: mSocketInfo(info),
mServerCert(serverCert.get()),
mCANamesStrings(std::move(caNamesStrings)),
mCollectedCANames(std::move(collectedCANames)),
mSelectedCertificate(nullptr),
mSelectedKey(nullptr) {}
@ -1751,40 +1754,25 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
private:
nsNSSSocketInfo* const mSocketInfo;
CERTCertificate* const mServerCert;
nsTArray<nsCString> mCANamesStrings;
nsTArray<nsTArray<uint8_t>> mCollectedCANames;
UniqueCERTCertificate mSelectedCertificate;
UniqueSECKEYPrivateKey mSelectedKey;
};
nsTArray<nsCString> DecodeCANames(CERTDistNames* caNames) {
nsTArray<nsTArray<uint8_t>> CollectCANames(CERTDistNames* caNames) {
MOZ_ASSERT(caNames);
nsTArray<nsCString> caNamesStrings;
nsTArray<nsTArray<uint8_t>> collectedCANames;
if (!caNames) {
return caNamesStrings;
return collectedCANames;
}
for (int i = 0; i < caNames->nnames; i++) {
char* caName = CERT_DerNameToAscii(&caNames->names[i]);
// Ignore failures
if (caName) {
caNamesStrings.AppendElement(nsCString(caName));
PORT_Free(caName); // CERT_DerNameToAscii uses PORT_Alloc
}
nsTArray<uint8_t> caName;
caName.AppendElements(caNames->names[i].data, caNames->names[i].len);
collectedCANames.AppendElement(std::move(caName));
}
return caNamesStrings;
}
nsTArray<char*> GetDependentStringPointers(nsTArray<nsCString>& strings) {
nsTArray<char*> pointers;
for (auto& string : strings) {
// We're not actually writing to the string. The API we're going to use this
// data with takes an array of non-const char pointers. To avoid the
// undefined behavior of reinterpreting a const pointer as a non-const
// pointer, everything we do here has to be non-const.
pointers.AppendElement(string.BeginWriting());
}
return pointers;
return collectedCANames;
}
// This callback function is used to pull client certificate
@ -1842,10 +1830,10 @@ SECStatus nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
return SECSuccess;
}
nsTArray<nsCString> caNamesStrings(DecodeCANames(caNames));
nsTArray<nsTArray<uint8_t>> collectedCANames(CollectCANames(caNames));
// XXX: This should be done asynchronously; see bug 696976
RefPtr<ClientAuthDataRunnable> runnable(
new ClientAuthDataRunnable(info, serverCert, caNamesStrings));
new ClientAuthDataRunnable(info, serverCert, collectedCANames));
nsresult rv = runnable->DispatchToMainThreadAndWait();
if (NS_FAILED(rv)) {
PR_SetError(SEC_ERROR_NO_MEMORY, 0);
@ -1868,6 +1856,214 @@ SECStatus nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
return SECSuccess;
}
// This TrustDomain only exists to facilitate the mozilla::pkix path building
// algorithm. It considers any certificate with an issuer distinguished name in
// the set of given CA names to be a trust anchor. It does essentially no
// validation or verification (in particular, the signature checking function
// always returns "Success").
class ClientAuthCertNonverifyingTrustDomain final : public TrustDomain {
public:
ClientAuthCertNonverifyingTrustDomain(
nsTArray<nsTArray<uint8_t>>& collectedCANames,
nsTArray<nsTArray<uint8_t>> thirdPartyIntermediates)
: mCollectedCANames(collectedCANames),
#ifdef MOZ_NEW_CERT_STORAGE
mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
#endif
mThirdPartyIntermediates(std::move(thirdPartyIntermediates)) {
}
virtual mozilla::pkix::Result GetCertTrust(
EndEntityOrCA endEntityOrCA, const CertPolicyId& policy,
Input candidateCertDER,
/*out*/ TrustLevel& trustLevel) override;
virtual mozilla::pkix::Result FindIssuer(Input encodedIssuerName,
IssuerChecker& checker,
Time time) override;
virtual mozilla::pkix::Result CheckRevocation(
EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
Time validityPeriodBeginning, Duration validityDuration,
/*optional*/ const Input* stapledOCSPresponse,
/*optional*/ const Input* aiaExtension) override {
return Success;
}
virtual mozilla::pkix::Result IsChainValid(
const DERArray& certChain, Time time,
const CertPolicyId& requiredPolicy) override {
return Success;
}
virtual mozilla::pkix::Result CheckSignatureDigestAlgorithm(
DigestAlgorithm digestAlg, EndEntityOrCA endEntityOrCA,
Time notBefore) override {
return Success;
}
virtual mozilla::pkix::Result CheckRSAPublicKeyModulusSizeInBits(
EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) override {
return Success;
}
virtual mozilla::pkix::Result VerifyRSAPKCS1SignedDigest(
const SignedDigest& signedDigest, Input subjectPublicKeyInfo) override {
return Success;
}
virtual mozilla::pkix::Result CheckECDSACurveIsAcceptable(
EndEntityOrCA endEntityOrCA, NamedCurve curve) override {
return Success;
}
virtual mozilla::pkix::Result VerifyECDSASignedDigest(
const SignedDigest& signedDigest, Input subjectPublicKeyInfo) override {
return Success;
}
virtual mozilla::pkix::Result CheckValidityIsAcceptable(
Time notBefore, Time notAfter, EndEntityOrCA endEntityOrCA,
KeyPurposeId keyPurpose) override {
return Success;
}
virtual mozilla::pkix::Result NetscapeStepUpMatchesServerAuth(
Time notBefore,
/*out*/ bool& matches) override {
matches = true;
return Success;
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) override {}
virtual mozilla::pkix::Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf,
size_t digestBufLen) override {
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
}
private:
nsTArray<nsTArray<uint8_t>>& mCollectedCANames; // non-owning
#ifdef MOZ_NEW_CERT_STORAGE
nsCOMPtr<nsICertStorage> mCertStorage;
#endif
nsTArray<nsTArray<uint8_t>> mThirdPartyIntermediates;
};
mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::GetCertTrust(
EndEntityOrCA endEntityOrCA, const CertPolicyId& policy,
Input candidateCertDER,
/*out*/ TrustLevel& trustLevel) {
// If the server did not specify any CA names, all client certificates are
// acceptable.
if (mCollectedCANames.Length() == 0) {
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
BackCert cert(candidateCertDER, endEntityOrCA, nullptr);
mozilla::pkix::Result rv = cert.Init();
if (rv != Success) {
return rv;
}
// If this certificate's issuer distinguished name is in the set of acceptable
// CA names, we say this is a trust anchor so that the client certificate
// issued from this certificate will be presented as an option for the user.
Input issuer(cert.GetIssuer());
for (const auto& caName : mCollectedCANames) {
Input caNameInput;
rv = caNameInput.Init(caName.Elements(), caName.Length());
if (rv != Success) {
continue; // probably too big
}
if (InputsAreEqual(issuer, caNameInput)) {
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
}
trustLevel = TrustLevel::InheritsTrust;
return Success;
}
mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::FindIssuer(
Input encodedIssuerName, IssuerChecker& checker, Time time) {
// First try all relevant certificates known to Gecko, which avoids calling
// CERT_CreateSubjectCertList, because that can be expensive.
Vector<Input> geckoIntermediateCandidates;
#ifdef MOZ_NEW_CERT_STORAGE
if (!mCertStorage) {
return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
nsTArray<uint8_t> subject;
subject.AppendElements(encodedIssuerName.UnsafeGetData(),
encodedIssuerName.GetLength());
nsTArray<nsTArray<uint8_t>> certs;
nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
if (NS_FAILED(rv)) {
return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
for (auto& cert : certs) {
Input certDER;
mozilla::pkix::Result rv = certDER.Init(cert.Elements(), cert.Length());
if (rv != Success) {
continue; // probably too big
}
// Currently we're only expecting intermediate certificates in cert storage.
if (!geckoIntermediateCandidates.append(certDER)) {
return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
}
}
#endif
for (const auto& thirdPartyIntermediate : mThirdPartyIntermediates) {
Input thirdPartyIntermediateInput;
mozilla::pkix::Result rv = thirdPartyIntermediateInput.Init(
thirdPartyIntermediate.Elements(), thirdPartyIntermediate.Length());
if (rv != Success) {
continue; // probably too big
}
if (!geckoIntermediateCandidates.append(thirdPartyIntermediateInput)) {
return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
}
}
bool keepGoing = true;
for (Input candidate : geckoIntermediateCandidates) {
mozilla::pkix::Result rv = checker.Check(candidate, nullptr, keepGoing);
if (rv != Success) {
return rv;
}
if (!keepGoing) {
return Success;
}
}
SECItem encodedIssuerNameItem = UnsafeMapInputToSECItem(encodedIssuerName);
// NSS seems not to differentiate between "no potential issuers found" and
// "there was an error trying to retrieve the potential issuers." We assume
// there was no error if CERT_CreateSubjectCertList returns nullptr.
UniqueCERTCertList candidates(CERT_CreateSubjectCertList(
nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameItem, 0, false));
Vector<Input> nssIntermediateCandidates;
if (candidates) {
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
Input certDER;
mozilla::pkix::Result rv =
certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
if (rv != Success) {
continue; // probably too big
}
if (!n->cert->isRoot) {
if (!nssIntermediateCandidates.append(certDER)) {
return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
}
}
}
}
for (Input candidate : nssIntermediateCandidates) {
mozilla::pkix::Result rv = checker.Check(candidate, nullptr, keepGoing);
if (rv != Success) {
return rv;
}
if (!keepGoing) {
return Success;
}
}
return Success;
}
void ClientAuthDataRunnable::RunOnTargetThread() {
// We check the value of a pref in this runnable, so this runnable should only
// be run on the main thread.
@ -1897,14 +2093,50 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
return;
}
nsTArray<char*> caNamesStringPointers(
GetDependentStringPointers(mCANamesStrings));
SECStatus srv = CERT_FilterCertListByCANames(
certList.get(), caNamesStringPointers.Length(),
caNamesStringPointers.Elements(), certUsageSSLClient);
if (NS_WARN_IF(srv != SECSuccess)) {
nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
if (NS_WARN_IF(!component)) {
return;
}
nsTArray<nsTArray<uint8_t>> enterpriseIntermediates;
nsresult rv = component->GetEnterpriseIntermediates(enterpriseIntermediates);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
ClientAuthCertNonverifyingTrustDomain trustDomain(
mCollectedCANames, std::move(enterpriseIntermediates));
CERTCertListNode* n = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(n, certList)) {
Input certDER;
mozilla::pkix::Result result =
certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
if (result != Success) {
CERTCertListNode* toRemove = n;
n = CERT_LIST_NEXT(n);
CERT_RemoveCertListNode(toRemove);
continue; // probably too big
}
mozilla::pkix::Result eeResult = BuildCertChain(
trustDomain, certDER, Now(), EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr);
mozilla::pkix::Result caResult = BuildCertChain(
trustDomain, certDER, Now(), EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr);
if (eeResult != Success && caResult != Success) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("client cert non-validation returned %d %d\n",
static_cast<int>(eeResult), static_cast<int>(caResult)));
CERTCertListNode* toRemove = n;
n = CERT_LIST_NEXT(n);
CERT_RemoveCertListNode(toRemove);
continue;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("keeping cert '%s'\n", n->cert->subjectName));
n = CERT_LIST_NEXT(n);
}
if (CERT_LIST_EMPTY(certList)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("no client certificates available after filtering by CA"));
@ -2019,9 +2251,8 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
// Throw up the client auth dialog and get back the index of the selected
// cert
nsresult rv =
getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsIClientAuthDialogs),
NS_CLIENTAUTHDIALOGS_CONTRACTID);
rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsIClientAuthDialogs),
NS_CLIENTAUTHDIALOGS_CONTRACTID);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}

View File

@ -9,6 +9,10 @@ support-files =
[browser_certViewer.js]
skip-if = verify
[browser_clientAuth_connection.js]
# Any test that has to delete certificates (e.g. as part of cleanup) is
# fundamentally incompatible with verify due to how NSS handles deleting
# certificates.
skip-if = verify
[browser_clientAuth_ui.js]
[browser_deleteCert_ui.js]
[browser_downloadCert_ui.js]

View File

@ -20,7 +20,9 @@ const DialogState = {
RETURN_CERT_NOT_SELECTED: "RETURN_CERT_NOT_SELECTED",
};
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
var gExpectedClientCertificateChoices;
// Mock implementation of nsIClientAuthDialogs.
const gClientAuthDialogs = {
@ -93,7 +95,11 @@ const gClientAuthDialogs = {
// selectable as well as one of the PGO certs we loaded in `setup`, so we do
// some brief checks to confirm this.
Assert.notEqual(certList, null, "Cert list should not be null");
Assert.equal(certList.length, 2, "2 certificate should be available");
Assert.equal(
certList.length,
gExpectedClientCertificateChoices,
`${gExpectedClientCertificateChoices} certificates should be available`
);
for (let cert of certList.enumerate(Ci.nsIX509Cert)) {
Assert.notEqual(cert, null, "Cert list should contain nsIX509Certs");
@ -129,6 +135,16 @@ add_task(async function setup() {
// This CA has all keyUsages. For compatibility with preexisting behavior, it
// will be presented for use as a client certificate.
await readCertificate("pgo-ca-all-usages.pem", "CTu,CTu,CTu");
// This client certificate was issued by an intermediate that was issued by
// the test CA. The server only lists the test CA's subject distinguished name
// as an acceptible issuer name for client certificates. If the implementation
// can determine that the test CA is a root CA for the client certificate and
// thus is acceptible to use, it should be included in the chooseCertificate
// callback. At the beginning of this test (speaking of this file as a whole),
// the client is not aware of the intermediate, and so it is not available in
// the callback.
await readCertificate("client-cert-via-intermediate.pem", ",,");
gExpectedClientCertificateChoices = 2;
});
/**
@ -165,7 +181,7 @@ async function testHelper(prefValue, expectedURL, options = undefined) {
let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec;
Assert.ok(
loadedURL.startsWith(expectedURL),
"Expected and actual URLs should match"
`Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')`
);
Assert.equal(
gClientAuthDialogs.chooseCertificateCalled,
@ -236,3 +252,36 @@ add_task(async function testClearPrivateBrowsingState() {
// when the last private window closes).
sdr.logoutAndTeardown();
});
// Test that 3rd party certificates are taken into account when filtering client
// certificates based on the acceptible CA list sent by the server.
add_task(async function testCertFilteringWithIntermediate() {
let intermediateBytes = await OS.File.read(
getTestFilePath("intermediate.pem")
).then(
data => {
let decoder = new TextDecoder();
let pem = decoder.decode(data);
let base64 = pemToBase64(pem);
let bin = atob(base64);
let bytes = [];
for (let i = 0; i < bin.length; i++) {
bytes.push(bin.charCodeAt(i));
}
return bytes;
},
error => {
throw error;
}
);
let nssComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsINSSComponent);
nssComponent.addEnterpriseIntermediate(intermediateBytes);
gExpectedClientCertificateChoices = 3;
gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED;
await testHelper("Ask Every Time", "https://requireclientcert.example.com/");
sdr.logoutAndTeardown();
// This will reset the added intermediate.
await SpecialPowers.pushPrefEnv({
set: [["security.enterprise_roots.enabled", true]],
});
});

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDETCCAfmgAwIBAgIUZ/wVsZBVUjlx32bqA0sXgHFnIU8wDQYJKoZIhvcNAQEL
BQAwQTEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEV
MBMGA1UECwwMSW50ZXJtZWRpYXRlMCIYDzIwMTgxMTI3MDAwMDAwWhgPMjAyMTAy
MDQwMDAwMDBaMCcxJTAjBgNVBAMMHGNsaWVudCBjZXJ0IHZpYSBpbnRlcm1lZGlh
dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
Ne2NAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUA
A4IBAQA9RUkBQgMo9f6cUsGbOmA+wEQeCvGBaLgdlIfRpCcjm7cXGbwi8U072bo+
fYw1Q9mKlN/S/RXCaixalxo2A4lVHJshDfM+SJ5wcY2OVKa2OxY3N/WMI3DlrfKK
FM6Sj2MHNvKAdTwyXZodSl7VOF0tdoYXL7x2Ac5Vfl0VMMxbBM1YrWUYPsZRc0qN
qSDQoB4CHB8cPxhGXx5rHgK9Pi3H4ZkjVNr0ZD9Tt1wso+G84FiIwQ/GKEIEtZ7B
VOm+rjvAXivPNx3neAvIVNIG2rfLTKbbCURvBytJ/PWuzDdN8JWxDA1wqEpxC5Wh
dMEsPmw3ChdQpqNN858qUSjKHWQf
-----END CERTIFICATE-----

View File

@ -0,0 +1,3 @@
issuer:/CN=Temporary Certificate Authority/OU=Intermediate
subject:client cert via intermediate
extension:extKeyUsage:clientAuth

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIUe9ala4zEws3CecubLRMUez2xnVowDQYJKoZIhvcNAQEL
BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
ZCBPcHRpbWl6YXRpb24wIhgPMjAxODExMjcwMDAwMDBaGA8yMDIxMDIwNDAwMDAw
MFowQTEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEV
MBMGA1UECwwMSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsG
A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAJ3KXbElUMtO7THRTY8wKYzit
cEuA1lwhO7qR7AkAjJIRcb+9qgT0gU8MesLxY1E6m5bpgWZB/pRRSaj702ahIlo/
2cLy9rahM/dU7u792m+KT7Phd5NOAPQIEuIJZzu/ux5OFZbsCE67xKJXEQ56pxwJ
H4fmsWaoDR8TtDBRONLsXVFy3gpfjhXOcdPEer9qmRz17pBRsB1M1ft3V4qnPJA5
VoxlNucTsdK2fwy/KYuuujnPGEI4ERcjElOoSsxVizFlf/LrFe3ibVVxfpGKMDvO
fsrffq2XhA2DnLYOlefJwiYS0Sz8thQN66cvdu489LMX0Lox8zFJgCvUJW01aA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,4 @@
issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
subject:/CN=Temporary Certificate Authority/OU=Intermediate
extension:basicConstraints:cA,
extension:keyUsage:keyCertSign,cRLSign

View File

@ -13,6 +13,7 @@ BROWSER_CHROME_MANIFESTS += ['browser.ini']
# when they're automatically generated.)
#test_certificates = (
# 'ca.pem',
# 'client-cert-via-intermediate.pem',
# 'code-ee.pem',
# 'ee-from-expired-ca.pem',
# 'ee-from-untrusted-ca.pem',
@ -23,6 +24,7 @@ BROWSER_CHROME_MANIFESTS += ['browser.ini']
# 'has-non-empty-subject.pem',
# 'has-o.pem',
# 'has-ou.pem',
# 'intermediate.pem',
# 'invalid.pem',
# 'longOID.pem',
# 'md5-ee.pem',

View File

@ -1,10 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIDgzCCAmugAwIBAgIUOZ+zojIZfz27KhBgkhPh1KNJKbYwDQYJKoZIhvcNAQEL
BQAwajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
MIIDgzCCAmugAwIBAgIUDQg2Gy5G9+g/H9GCUkYVM1qnf8IwDQYJKoZIhvcNAQEL
BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
ZCBPcHRpbWl6YXRpb24wIhgPMjAxODExMjcwMDAwMDBaGA8yMDIxMDIwNDAwMDAw
MFowajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
@ -12,10 +12,10 @@ iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
AgH+MA0GCSqGSIb3DQEBCwUAA4IBAQC0MDDRTjbHbKLpm6LLLMQ7odjuq3q/JZBr
YmjWFTd84v0yUUzmwYNCSwq1X69mYkopOaz2xPVNc7maj0jeJY0QvWT8q4pIMbaX
9lRAJBWY0WNXATJqZrdgc2JGfwHIIFRn1V+AUFX3mGv23ev2G5RHxepbh288Kaxu
kHtX9A2xDPGV4oFo/BhfRjImA2WHfqkY9Wcpm9BBD8SesZ9TSOLvyZT+8dVBeO0z
Rv6IPiV4Wc2Pg2AiZqTPDH9jZsKezxURUorKWh9eatmpIVpQAsKueXXBZfwKpZo5
9SodlHNv4QL1Nxdu+yIcL6kgeClKLRclBGaj7FF7d6228CB/G6Dk
AgH+MA0GCSqGSIb3DQEBCwUAA4IBAQBKtiqhwcyJShE77Uekj13k1483kc5hyNPN
z3nDXWGA03M0IBYB6z5Fec/EuZp853N/MN1mnmHxWDsFH5C13a2aratr6bMrxaRf
a+c2lvM023sulllKIYpLJSA67h7+qbbNv0hgQzyyE3Rjdjngh8b6ZCjm/D0GO/u6
T8O+pzbekAWidE8TYPVP36cFzfzKxYTm7O5J2t9ilqFFw4NNHaQQt1JP+yk4B0MJ
Gp+VBRDMMBTIZzR9AO1w85HMa/WwDs7Jj1vuxz0vjMhVfBMlgAzc5lum3Zde9mlp
sAwLvmgPnNKL+bogu7YcE/xee9cM/lOW1AfiJlszssozcLKLz6/7
-----END CERTIFICATE-----

View File

@ -1,4 +1,4 @@
issuer:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
subject:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
extension:basicConstraints:cA,
extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign

View File

@ -1,10 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIDgzCCAmugAwIBAgIUacGJyguVOrRdJ0sThPQuT28Rb+0wDQYJKoZIhvcNAQEL
BQAwajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
MIIDgzCCAmugAwIBAgIUM3Lmh+4Pw8Pfiu1leYvuN3iWevEwDQYJKoZIhvcNAQEL
BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
ZCBPcHRpbWl6YXRpb24wIhgPMjAxODExMjcwMDAwMDBaGA8yMDIxMDIwNDAwMDAw
MFowajEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UECgwPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLDBtQcm9maWxlIEd1aWRl
MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
@ -12,10 +12,10 @@ iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQB1BJFL+ylvUUeKwIK/CLHjIi91qV7eQ4sw
U4puUjrrMep8DqHEhISSHOo6JREr82JWsQ82+qjiNlZCcSv28twEYqdkCb067tep
fmgQ+/jGVNfFAzI+m9RDbad54O7i6tI4+dYoq7RoZkFiyTAAIWZ0ic9vifryABsa
SvmdQ5loxdhdSY24VDE/emnvBXYknWk7+QbxauoH0bPTsJvuuoutorGxDPN3s+cv
6LkX8Dlgg8wemSOQY1ZP1eY9jWEvVCKoDMeLKOcuuzeUryLjs/KOR1h1VEFTm97c
L01TfBaLzVX3vFAQjmriF4Bd5/FM0jCcjFSVDVZ4GmzVX7XUVhyd
AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBzEAci/+VfRVPmd0Gc0g+1iwf2lGeGaN4o
cvqKQQHdh6D0dqOFqXYeNsOELWKwgIRx8OX1MlvRQMH58ZB8bY1a+drIPALkIuWv
ORWVLdNscJ+IT3wsscCWP6t3O64bBLbTonW1odcdVxez1uHBvRYZXYSc5U/D55PE
Tf+ZU2yy6BzlJ2IftwS4M3Virny+SEE0lcBwP0m0Vz7+08K7apW2Yh/38VLukd6a
Kv7JkcgoK/IeZXazlmG0Bxs+IpWlF239HZ4EvpZIxQBbcS68iNoqqL6OB9yycLay
KzjejGBHyKXvtUKSEIOt84KFoEKio/cdYlv3xlfQ/LSe0AVdlJR5
-----END CERTIFICATE-----

View File

@ -1,4 +1,4 @@
issuer:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
subject:/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
extension:basicConstraints:cA,
extension:keyUsage:keyCertSign,cRLSign