bug 1166976 - generate some PSM xpcshell test certificates at build time r=Cykesiopka,mgoodwin,froydnj
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 20 May 2015 16:35:16 -0700
changeset 246205 ec1e8a7a36102c7284565485235a065b15ef78f0
parent 246204 6b9de503879547f9659915596f858bf71df7ed8c
child 246206 71a80e43615cd6b1d4c86c23a9106cf6d780ccfb
push id28823
push userryanvm@gmail.com
push dateFri, 29 May 2015 13:33:16 +0000
treeherdermozilla-central@9738f055d98c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCykesiopka, mgoodwin, froydnj
bugs1166976
milestone41.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
bug 1166976 - generate some PSM xpcshell test certificates at build time r=Cykesiopka,mgoodwin,froydnj
build/virtualenv_packages.txt
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/moz.build
security/manager/ssl/tests/unit/pycert.py
security/manager/ssl/tests/unit/test_add_preexisting_cert.js
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/generate.py
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.der
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem.certspec
security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -20,11 +20,13 @@ mozilla.pth:dom/bindings
 mozilla.pth:dom/bindings/parser
 mozilla.pth:layout/tools/reftest
 moztreedocs.pth:tools/docs
 copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
 objdir:build
 gyp.pth:media/webrtc/trunk/tools/gyp/pylib
 pyasn1.pth:python/pyasn1
+pyasn1_modules.pth:python/pyasn1-modules
 bitstring.pth:python/bitstring
 redo.pth:python/redo
 requests.pth:python/requests
+rsa.pth:python/rsa
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -83,29 +83,54 @@ const certificateUsageSSLCA             
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
 const certificateUsageObjectSigner           = 0x0040;
 const certificateUsageVerifyCA               = 0x0100;
 const certificateUsageStatusResponder        = 0x0400;
 
 const NO_FLAGS = 0;
 
+// Commonly certificates are represented as PEM. The format is roughly as
+// follows:
+//
+// -----BEGIN CERTIFICATE-----
+// [some lines of base64, each typically 64 characters long]
+// -----END CERTIFICATE-----
+//
+// However, nsIX509CertDB.constructX509FromBase64 and related functions do not
+// handle input of this form. Instead, they require a single string of base64
+// 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 readFile(file) {
   let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
                   .createInstance(Ci.nsIFileInputStream);
   fstream.init(file, -1, 0, 0);
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
   fstream.close();
   return data;
 }
 
 function addCertFromFile(certdb, filename, trustString) {
   let certFile = do_get_file(filename, false);
-  let der = readFile(certFile);
-  certdb.addCert(der, trustString, null);
+  let certBytes = readFile(certFile);
+  let successful = false;
+  try {
+    certdb.addCert(certBytes, trustString, null);
+    successful = true;
+  } catch (e) {}
+  if (!successful) {
+    // It might be PEM instead of DER.
+    certdb.addCertFromBase64(pemToBase64(certBytes), trustString, null);
+  }
 }
 
 function constructCertFromFile(filename) {
   let certFile = do_get_file(filename, false);
   let certDER = readFile(certFile);
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
   return certdb.constructX509(certDER, certDER.length);
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -1,10 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['tlsserver']
+TEST_DIRS += ['test_intermediate_basic_usage_constraints']
 
 if not CONFIG['MOZ_NO_SMART_CARDS']:
     DIRS += ['pkcs11testmodule']
new file mode 100755
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -0,0 +1,398 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Reads a certificate specification from stdin or a file and outputs a
+signed x509 certificate with the desired properties.
+
+The input format is as follows:
+
+issuer:<string to use as the issuer common name>
+subject:<string to use as the subject common name>
+[extension:<extension name:<extension-specific data>>]
+[...]
+
+Known extensions are:
+basicConstraints:[cA],[pathLenConstraint]
+keyUsage:[digitalSignature,nonRepudiation,keyEncipherment,
+          dataEncipherment,keyAgreement,keyCertSign,cRLSign]
+extKeyUsage:[serverAuth,clientAuth]
+
+In the future it will be possible to specify other properties of the
+generated certificate (for example, its validity period, signature
+algorithm, etc.). For now, those fields have reasonable default values.
+Currently one shared RSA key is used for all signatures.
+"""
+
+from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
+from pyasn1.type import constraint, namedtype, tag, univ, useful
+from pyasn1_modules import rfc2459
+import base64
+import binascii
+import datetime
+import hashlib
+import sys
+import rsa
+
+class UnknownBaseError(Exception):
+    """Base class for handling unexpected input in this module."""
+    def __init__(self, value):
+        self.value = value
+        self.category = 'input'
+
+    def __str__(self):
+        return 'Unknown %s type "%s"' % (self.category, repr(self.value))
+
+
+class UnknownAlgorithmTypeError(UnknownBaseError):
+    """Helper exception type to handle unknown algorithm types."""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'algorithm'
+
+
+class UnknownParameterTypeError(UnknownBaseError):
+    """Helper exception type to handle unknown input parameters."""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'parameter'
+
+
+class UnknownExtensionTypeError(UnknownBaseError):
+    """Helper exception type to handle unknown input extensions."""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'extension'
+
+
+class UnknownKeyPurposeTypeError(UnknownBaseError):
+    """Helper exception type to handle unknown key purposes."""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'keyPurpose'
+
+
+def getASN1Tag(asn1Type):
+    """Helper function for returning the base tag value of a given
+    type from the pyasn1 package"""
+    return asn1Type.baseTagSet.getBaseTag().asTuple()[2]
+
+def stringToAlgorithmIdentifier(string):
+    """Helper function that converts a description of an algorithm
+    to a representation usable by the pyasn1 package"""
+    algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+    algorithm = None
+    if string == 'sha256WithRSAEncryption':
+        algorithm = univ.ObjectIdentifier('1.2.840.113549.1.1.11')
+    # In the future, more algorithms will be supported.
+    if algorithm == None:
+        raise UnknownAlgorithmTypeError(string)
+    algorithmIdentifier.setComponentByName('algorithm', algorithm)
+    return algorithmIdentifier
+
+def stringToCommonName(string):
+    """Helper function for taking a string and building an x520 name
+    representation usable by the pyasn1 package. Currently returns one
+    RDN with one AVA consisting of a Common Name encoded as a
+    UTF8String."""
+    commonName = rfc2459.X520CommonName()
+    commonName.setComponentByName('utf8String', string)
+    ava = rfc2459.AttributeTypeAndValue()
+    ava.setComponentByName('type', rfc2459.id_at_commonName)
+    ava.setComponentByName('value', commonName)
+    rdn = rfc2459.RelativeDistinguishedName()
+    rdn.setComponentByPosition(0, ava)
+    rdns = rfc2459.RDNSequence()
+    rdns.setComponentByPosition(0, rdn)
+    name = rfc2459.Name()
+    name.setComponentByPosition(0, rdns)
+    return name
+
+def datetimeToTime(dt):
+    """Takes a datetime object and returns an rfc2459.Time object with
+    that time as its value as a GeneralizedTime"""
+    time = rfc2459.Time()
+    time.setComponentByName('generalTime', useful.GeneralizedTime(dt.strftime('%Y%m%d%H%M%SZ')))
+    return time
+
+def byteStringToHexifiedBitString(string):
+    """Takes a string of bytes and returns a hex string representing
+    those bytes for use with pyasn1.type.univ.BitString. It must be of
+    the form "'<hex bytes>'H", where the trailing 'H' indicates to
+    pyasn1 that the input is a hex string."""
+    return "'%s'H" % binascii.hexlify(string)
+
+class RSAPublicKey(univ.Sequence):
+    """Helper type for encoding an RSA public key"""
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('N', univ.Integer()),
+        namedtype.NamedType('E', univ.Integer()))
+
+
+class Certificate:
+    """Utility class for reading a certificate specification and
+    generating a signed x509 certificate"""
+
+    sharedRSA_N = long(
+        '00ba8851a8448e16d641fd6eb6880636103d3c13d9eae4354ab4ecf56857'
+        '6c247bc1c725a8e0d81fbdb19c069b6e1a86f26be2af5a756b6a6471087a'
+        'a55aa74587f71cd5249c027ecd43fc1e69d038202993ab20c349e4dbb94c'
+        'c26b6c0eed15820ff17ead691ab1d3023a8b2a41eea770e00f0d8dfd660b'
+        '2bb02492a47db988617990b157903dd23bc5e0b8481fa837d38843ef2716'
+        'd855b7665aaa7e02902f3a7b10800624cc1c6c97ad96615bb7e29612c075'
+        '31a30c91ddb4caf7fcad1d25d309efb9170ea768e1b37b2f226f69e3b48a'
+        '95611dee26d6259dab91084e36cb1c24042cbf168b2fe5f18f991731b8b3'
+        'fe4923fa7251c431d503acda180a35ed8d', 16)
+    sharedRSA_E = 65537L
+    sharedRSA_D = long(
+        '009ecbce3861a454ecb1e0fe8f85dd43c92f5825ce2e997884d0e1a949da'
+        'a2c5ac559b240450e5ac9fe0c3e31c0eefa6525a65f0c22194004ee1ab46'
+        '3dde9ee82287cc93e746a91929c5e6ac3d88753f6c25ba5979e73e5d8fb2'
+        '39111a3cdab8a4b0cdf5f9cab05f1233a38335c64b5560525e7e3b92ad7c'
+        '7504cf1dc7cb005788afcbe1e8f95df7402a151530d5808346864eb370aa'
+        '79956a587862cb533791307f70d91c96d22d001a69009b923c683388c9f3'
+        '6cb9b5ebe64302041c78d908206b87009cb8cabacad3dbdb2792fb911b2c'
+        'f4db6603585be9ae0ca3b8e6417aa04b06e470ea1a3b581ca03a6781c931'
+        '5b62b30e6011f224725946eec57c6d9441', 16)
+    sharedRSA_P = long(
+        '00dd6e1d4fffebf68d889c4d114cdaaa9caa63a59374286c8a5c29a717bb'
+        'a60375644d5caa674c4b8bc7326358646220e4550d7608ac27d55b6db74f'
+        '8d8127ef8fa09098b69147de065573447e183d22fe7d885aceb513d9581d'
+        'd5e07c1a90f5ce0879de131371ecefc9ce72e9c43dc127d238190de81177'
+        '3ca5d19301f48c742b', 16)
+    sharedRSA_Q = long(
+        '00d7a773d9ebc380a767d2fec0934ad4e8b5667240771acdebb5ad796f47'
+        '8fec4d45985efbc9532968289c8d89102fadf21f34e2dd4940eba8c09d6d'
+        '1f16dcc29729774c43275e9251ddbe4909e1fd3bf1e4bedf46a39b8b3833'
+        '28ef4ae3b95b92f2070af26c9e7c5c9b587fedde05e8e7d86ca57886fb16'
+        '5810a77b9845bc3127', 16)
+
+    def __init__(self, paramStream, now=datetime.datetime.utcnow()):
+        self.version = 'v3'
+        self.signature = 'sha256WithRSAEncryption'
+        self.issuer = 'Default Issuer'
+        oneYear = datetime.timedelta(days=365)
+        self.notBefore = now - oneYear
+        self.notAfter = now + oneYear
+        self.subject = 'Default Subject'
+        self.signatureAlgorithm = 'sha256WithRSAEncryption'
+        self.extensions = None
+        self.decodeParams(paramStream)
+        self.serialNumber = self.generateSerialNumber()
+
+    def generateSerialNumber(self):
+        """Generates a serial number for this certificate based on its
+        contents. Intended to be reproducible for compatibility with
+        the build system on OS X (see the comment above main, later in
+        this file)."""
+        hasher = hashlib.sha256()
+        hasher.update(self.version)
+        hasher.update(self.signature)
+        hasher.update(self.issuer)
+        hasher.update(str(self.notBefore))
+        hasher.update(str(self.notAfter))
+        hasher.update(self.subject)
+        hasher.update(self.signatureAlgorithm)
+        if self.extensions:
+            for extension in self.extensions:
+                hasher.update(str(extension))
+        serialBytes = [ord(c) for c in hasher.digest()[:20]]
+        # Ensure that the most significant bit isn't set (which would
+        # indicate a negative number, which isn't valid for serial
+        # numbers).
+        serialBytes[0] &= 0x7f
+        # Also ensure that the least significant bit on the most
+        # significant byte is set (to prevent a leading zero byte,
+        # which also wouldn't be valid).
+        serialBytes[0] |= 0x01
+        # Now prepend the ASN.1 INTEGER tag and length bytes.
+        serialBytes.insert(0, len(serialBytes))
+        serialBytes.insert(0, getASN1Tag(univ.Integer))
+        return ''.join(chr(b) for b in serialBytes)
+
+    def decodeParams(self, paramStream):
+        for line in paramStream.readlines():
+            self.decodeParam(line.strip())
+
+    def decodeParam(self, line):
+        param = line.split(':')[0]
+        value = ':'.join(line.split(':')[1:])
+        if param == 'subject':
+            self.subject = value
+        elif param == 'issuer':
+            self.issuer = value
+        elif param == 'extension':
+            self.decodeExtension(value)
+        else:
+            raise UnknownParameterTypeError(param)
+
+    def decodeExtension(self, extension):
+        extensionType = extension.split(':')[0]
+        value = ':'.join(extension.split(':')[1:])
+        if extensionType == 'basicConstraints':
+            self.addBasicConstraints(value)
+        elif extensionType == 'keyUsage':
+            self.addKeyUsage(value)
+        elif extensionType == 'extKeyUsage':
+            self.addExtKeyUsage(value)
+        else:
+            raise UnknownExtensionTypeError(extensionType)
+
+    def addExtension(self, extensionType, extensionValue):
+        if not self.extensions:
+            self.extensions = []
+        encapsulated = univ.OctetString(encoder.encode(extensionValue))
+        extension = rfc2459.Extension()
+        extension.setComponentByName('extnID', extensionType)
+        extension.setComponentByName('extnValue', encapsulated)
+        self.extensions.append(extension)
+
+    def addBasicConstraints(self, basicConstraints):
+        cA = basicConstraints.split(',')[0]
+        pathLenConstraint = basicConstraints.split(',')[1]
+        basicConstraintsExtension = rfc2459.BasicConstraints()
+        basicConstraintsExtension.setComponentByName('cA', cA == 'cA')
+        if pathLenConstraint:
+            pathLenConstraintValue = \
+                univ.Integer(int(pathLenConstraint)).subtype(
+                    subtypeSpec=constraint.ValueRangeConstraint(0, 64))
+            basicConstraintsExtension.setComponentByName('pathLenConstraint',
+                                                         pathLenConstraintValue)
+        self.addExtension(rfc2459.id_ce_basicConstraints, basicConstraintsExtension)
+
+    def addKeyUsage(self, keyUsage):
+        keyUsageExtension = rfc2459.KeyUsage(keyUsage)
+        self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension)
+
+    def keyPurposeToOID(self, keyPurpose):
+        if keyPurpose == 'serverAuth':
+            # the OID for id_kp_serverAuth is incorrect in the
+            # pyasn1-modules implementation
+            return univ.ObjectIdentifier('1.3.6.1.5.5.7.3.1')
+        if keyPurpose == 'clientAuth':
+            return rfc2459.id_kp_clientAuth
+        raise UnknownKeyPurposeTypeError(keyPurpose)
+
+    def addExtKeyUsage(self, extKeyUsage):
+        extKeyUsageExtension = rfc2459.ExtKeyUsageSyntax()
+        count = 0
+        for keyPurpose in extKeyUsage.split(','):
+            extKeyUsageExtension.setComponentByPosition(count, self.keyPurposeToOID(keyPurpose))
+            count += 1
+        self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension)
+
+    def getVersion(self):
+        return rfc2459.Version(self.version).subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
+
+    def getSerialNumber(self):
+        return decoder.decode(self.serialNumber)[0]
+
+    def getSignature(self):
+        return stringToAlgorithmIdentifier(self.signature)
+
+    def getIssuer(self):
+        return stringToCommonName(self.issuer)
+
+    def getValidity(self):
+        validity = rfc2459.Validity()
+        validity.setComponentByName('notBefore', self.getNotBefore())
+        validity.setComponentByName('notAfter', self.getNotAfter())
+        return validity
+
+    def getNotBefore(self):
+        return datetimeToTime(self.notBefore)
+
+    def getNotAfter(self):
+        return datetimeToTime(self.notAfter)
+
+    def getSubject(self):
+        return stringToCommonName(self.subject)
+
+    def getSignatureAlgorithm(self):
+        return stringToAlgorithmIdentifier(self.signature)
+
+    def getSubjectPublicKey(self):
+        rsaKey = RSAPublicKey()
+        rsaKey.setComponentByName('N', univ.Integer(self.sharedRSA_N))
+        rsaKey.setComponentByName('E', univ.Integer(self.sharedRSA_E))
+        return univ.BitString(byteStringToHexifiedBitString(encoder.encode(rsaKey)))
+
+    def getSubjectPublicKeyInfo(self):
+        algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+        algorithmIdentifier.setComponentByName('algorithm', rfc2459.rsaEncryption)
+        algorithmIdentifier.setComponentByName('parameters', univ.Null())
+        spki = rfc2459.SubjectPublicKeyInfo()
+        spki.setComponentByName('algorithm', algorithmIdentifier)
+        spki.setComponentByName('subjectPublicKey', self.getSubjectPublicKey())
+        return spki
+
+    def toDER(self):
+        tbsCertificate = rfc2459.TBSCertificate()
+        tbsCertificate.setComponentByName('version', self.getVersion())
+        tbsCertificate.setComponentByName('serialNumber', self.getSerialNumber())
+        tbsCertificate.setComponentByName('signature', self.getSignature())
+        tbsCertificate.setComponentByName('issuer', self.getIssuer())
+        tbsCertificate.setComponentByName('validity', self.getValidity())
+        tbsCertificate.setComponentByName('subject', self.getSubject())
+        tbsCertificate.setComponentByName('subjectPublicKeyInfo', self.getSubjectPublicKeyInfo())
+        if self.extensions:
+            extensions = rfc2459.Extensions().subtype(
+                explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
+            count = 0
+            for extension in self.extensions:
+                extensions.setComponentByPosition(count, extension)
+                count += 1
+            tbsCertificate.setComponentByName('extensions', extensions)
+        tbsDER = encoder.encode(tbsCertificate)
+        rsaPrivateKey = rsa.PrivateKey(self.sharedRSA_N, self.sharedRSA_E, self.sharedRSA_D,
+                                       self.sharedRSA_P, self.sharedRSA_Q)
+        signature = rsa.sign(tbsDER, rsaPrivateKey, 'SHA-256')
+        certificate = rfc2459.Certificate()
+        certificate.setComponentByName('tbsCertificate', tbsCertificate)
+        certificate.setComponentByName('signatureAlgorithm', self.getSignatureAlgorithm())
+        certificate.setComponentByName('signatureValue', byteStringToHexifiedBitString(signature))
+        return encoder.encode(certificate)
+
+    def toPEM(self):
+        output = '-----BEGIN CERTIFICATE-----'
+        der = self.toDER()
+        b64 = base64.b64encode(der)
+        while b64:
+            output += '\n' + b64[:64]
+            b64 = b64[64:]
+        output += '\n-----END CERTIFICATE-----'
+        return output
+
+
+# The build harness will call this function with an output file-like
+# object, a path to a file containing a specification, and the path to
+# the directory containing the buildid file. This will read the
+# specification and output the certificate as PEM. The purpose of the
+# buildid file is to provide a single definition of 'now'. This is
+# particularly important when building on OS X, where we generate
+# everything twice for unified builds. During the unification step, if
+# any pair of input files differ, the build system throws an error.
+# While it would make the most sense to provide the path to the buildid
+# file itself, since it doesn't exist when processing moz.build files
+# (but it does exist when actually running this script), the build
+# system won't let us pass it in directly.
+def main(output, inputPath, buildIDPath):
+    with open('%s/buildid' % buildIDPath) as buildidFile:
+        buildid = buildidFile.read().strip()
+    now = datetime.datetime.strptime(buildid, '%Y%m%d%H%M%S')
+    with open(inputPath) as configStream:
+        output.write(Certificate(configStream, now=now).toPEM())
+
+# When run as a standalone program, this will read a specification from
+# stdin and output the certificate as PEM to stdout.
+if __name__ == '__main__':
+    print Certificate(sys.stdin).toPEM()
--- a/security/manager/ssl/tests/unit/test_add_preexisting_cert.js
+++ b/security/manager/ssl/tests/unit/test_add_preexisting_cert.js
@@ -2,17 +2,17 @@
 /* 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/. */
 do_get_profile();
 let certDB = Cc["@mozilla.org/security/x509certdb;1"]
                .getService(Ci.nsIX509CertDB);
 
 function load_cert(cert, trust) {
-  let file = "test_intermediate_basic_usage_constraints/" + cert + ".der";
+  let file = "test_intermediate_basic_usage_constraints/" + cert + ".pem";
   addCertFromFile(certDB, file, trust);
 }
 
 function getDERString(cert)
 {
   var length = {};
   var cert_der = cert.getRawDER(length);
   var cert_der_string = '';
@@ -20,19 +20,19 @@ function getDERString(cert)
     cert_der_string += String.fromCharCode(cert_der[i]);
   }
   return cert_der_string;
 }
 
 function run_test() {
   load_cert("ca", "CTu,CTu,CTu");
   load_cert("int-limited-depth", "CTu,CTu,CTu");
-  let file = "test_intermediate_basic_usage_constraints/ee-int-limited-depth.der";
-  let cert_der = readFile(do_get_file(file));
-  let ee = certDB.constructX509(cert_der, cert_der.length);
+  let file = "test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem";
+  let cert_pem = readFile(do_get_file(file));
+  let ee = certDB.constructX509FromBase64(pemToBase64(cert_pem));
   checkCertErrorGeneric(certDB, ee, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   // Change the already existing intermediate certificate's trust using
   // addCertFromBase64(). We use findCertByNickname first to ensure that the
   // certificate already exists.
   let int_cert = certDB.findCertByNickname(null, "int-limited-depth");
   ok(int_cert);
   let base64_cert = btoa(getDERString(int_cert));
--- a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
@@ -1,55 +1,39 @@
 "use strict";
-/* To regenerate the certificates for this test:
- *
- *      cd security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints
- *      ./generate.py
- *      cd ../../../../../..
- *      make -C $OBJDIR/security/manager/ssl/tests
- *
- * Check in the generated files. These steps are not done as part of the build
- * because we do not want to add a build-time dependency on the OpenSSL or NSS
- * tools or libraries built for the host platform.
- */
 
 do_get_profile(); // must be called before getting nsIX509CertDB
 const certdb = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
 
 function load_cert(name, trust) {
-  let filename = "test_intermediate_basic_usage_constraints/" + name + ".der";
+  let filename = "test_intermediate_basic_usage_constraints/" + name + ".pem";
   addCertFromFile(certdb, filename, trust);
 }
 
 function test_cert_for_usages(certChainNicks, expected_usages_string) {
   let certs = [];
   for (let i in certChainNicks) {
     let certNick = certChainNicks[i];
-    let certDER = readFile(do_get_file(
-                             "test_intermediate_basic_usage_constraints/"
-                                + certNick + ".der"), false);
-    certs.push(certdb.constructX509(certDER, certDER.length));
+    let certPEM = readFile(
+                    do_get_file("test_intermediate_basic_usage_constraints/"
+                                + certNick + ".pem"), false);
+    certs.push(certdb.constructX509FromBase64(pemToBase64(certPEM)));
   }
 
   let cert = certs[0];
   let verified = {};
   let usages = {};
   cert.getUsagesString(true, verified, usages);
   do_print("usages.value = " + usages.value);
   do_check_eq(expected_usages_string, usages.value);
 }
 
 function run_test() {
-  // mozilla::pkix doesn't support the obsolete Netscape object signing
-  // extension, but NSS does.
   let ee_usage1 = 'Client,Server,Sign,Encrypt,Object Signer';
-
-  // mozilla::pkix doesn't validate CA certificates for non-CA uses, but
-  // NSS does.
   let ca_usage1 = "SSL CA";
 
   // Load the ca into mem
   let ca_name = "ca";
   load_cert(ca_name, "CTu,CTu,CTu");
   do_print("ca_name = " + ca_name);
   test_cert_for_usages([ca_name], ca_usage1);
 
@@ -80,34 +64,31 @@ function run_test() {
   // chain to to the CA cert.
   test_cert_for_usages(["ee-int-limited-depth", "int-limited-depth"],
                        ee_usage1);
 
   // ca
   //   int-limited-depth (cA==true, pathLenConstraint==0)
   //      int-limited-depth-invalid (cA==true)
   //
-  // XXX: It seems the NSS code does not consider the path length of the
-  // certificate we're validating, but mozilla::pkix does. mozilla::pkix's
-  // behavior is correct.
   test_cert_for_usages(["int-limited-depth-invalid", "int-limited-depth"], "");
   test_cert_for_usages(["ee-int-limited-depth-invalid",
                         "int-limited-depth-invalid",
                         "int-limited-depth"],
                        "");
 
   // int-valid-ku-no-eku has keyCertSign
   test_cert_for_usages(["int-valid-ku-no-eku"], "SSL CA");
   test_cert_for_usages(["ee-int-valid-ku-no-eku", "int-valid-ku-no-eku"],
                        ee_usage1);
 
   // int-bad-ku-no-eku has basicConstraints.cA==true and has a KU extension
   // but the KU extension is missing keyCertSign. Note that mozilla::pkix
   // doesn't validate certificates with basicConstraints.Ca==true for non-CA
-  // uses, but NSS does.
+  // uses.
   test_cert_for_usages(["int-bad-ku-no-eku"], "");
   test_cert_for_usages(["ee-int-bad-ku-no-eku", "int-bad-ku-no-eku"], "");
 
   // int-no-ku-no-eku has basicConstraints.cA==true and no KU extension.
   // We treat a missing KU as "any key usage is OK".
   test_cert_for_usages(["int-no-ku-no-eku"], ca_usage1);
   test_cert_for_usages(["ee-int-no-ku-no-eku", "int-no-ku-no-eku"], ee_usage1);
 
deleted file mode 100644
index da8561f525f603de28acc7f9f260f4cf7536215f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
deleted file mode 100644
index 5d1b8398e1bb35a4021f3c867604cd0c3daac493..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-bad-ku-no-eku
+subject:ee-int-bad-ku-no-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 032ed8288db7f51d3cc1eee02fe9ef8a1aeb76a0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-bad-ku-server-eku
+subject:ee-int-bad-ku-server-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 485d86d477f489175f8715aca9ac4e77f3f85989..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-cA-FALSE-asserts-keyCertSign
+subject:ee-int-cA-FALSE-asserts-keyCertSign
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index a3d7d0fdcac0555f735ded523dbe421c6d6fbdef..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-limited-depth-invalid
+subject:ee-int-limited-depth-invalid
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 88e3fa6224e83816cd67b0fee410ca80d03425e4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-limited-depth
+subject:ee-int-limited-depth
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 4fb37a3207dee7762fbc3775057fe4831622ccf9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-no-extensions
+subject:ee-int-no-extensions
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index f488b356412820b0ecbd52c2654200de1e75ddd4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-no-ku-no-eku
+subject:ee-int-no-ku-no-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 0485b47e6cf40651e70f5146fb129800ece489b8..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-no-ku-server-eku
+subject:ee-int-no-ku-server-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 495f8317f118ef6d996125fa5cbbe0d8cf495d42..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-not-a-ca
+subject:ee-int-not-a-ca
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index 9f17226af07362d565cc08c799ceb27bdc0f9178..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-valid-ku-no-eku
+subject:ee-int-valid-ku-no-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
index fc4d4dc897bcfc997e22e63d3dfe907514475807..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-valid-ku-server-eku
+subject:ee-int-valid-ku-server-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/generate.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/python
-
-# 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/.
-
-# requires openssl >= 1.0.0
-
-import tempfile, os, sys, random
-libpath = os.path.abspath('../psm_common_py')
-
-sys.path.append(libpath)
-
-import CertUtils
-
-
-srcdir = os.getcwd()
-db = tempfile.mkdtemp()
-
-CA_basic_constraints = "basicConstraints = critical, CA:TRUE\n"
-CA_limited_basic_constraints = "basicConstraints = critical,CA:TRUE, pathlen:0\n"
-EE_basic_constraints = "basicConstraints = CA:FALSE\n"
-
-CA_min_ku = "keyUsage = critical, keyCertSign\n"
-CA_bad_ku = "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, cRLSign\n"
-all_ku = "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign\n"
-EE_full_ku ="keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement\n"
-
-Server_eku = "extendedKeyUsage = critical, serverAuth, clientAuth\n "
-
-key_type = 'rsa'
-
-def generate_int_and_ee2(ca_key, ca_cert, suffix, int_ext_text, ee_ext_text):
-    int_name = "int-" + suffix;
-    ee_name  = "ee-int-" + suffix;
-    int_serial = random.randint(100, 40000000);
-    ee_serial = random.randint(100, 40000000);
-    [int_key, int_cert] = CertUtils.generate_cert_generic(db,
-                                                          srcdir,
-                                                          int_serial,
-                                                          key_type,
-                                                          int_name,
-                                                          int_ext_text,
-                                                          ca_key,
-                                                          ca_cert);
-    [ee_key, ee_cert] = CertUtils.generate_cert_generic(db,
-                                                        srcdir,
-                                                        ee_serial,
-                                                        key_type,
-                                                        ee_name,
-                                                        ee_ext_text,
-                                                        int_key,
-                                                        int_cert);
-    return [int_key, int_cert, ee_key, ee_cert]
-
-def generate_certs():
-    ca_name = "ca"
-    [ca_key, ca_cert ] = CertUtils.generate_cert_generic(db,
-                                                        srcdir,
-                                                        1,
-                                                        key_type,
-                                                        ca_name,
-                                                        CA_basic_constraints)
-    ee_ext_text = EE_basic_constraints + EE_full_ku
-
-    #now the intermediates
-    generate_int_and_ee2(ca_key, ca_cert, "no-extensions", "", ee_ext_text)
-    generate_int_and_ee2(ca_key, ca_cert, "not-a-ca", EE_basic_constraints,
-                         ee_ext_text)
-    generate_int_and_ee2(ca_key, ca_cert, "cA-FALSE-asserts-keyCertSign",
-                         EE_basic_constraints + all_ku, ee_ext_text)
-
-    [int_key, int_cert, a, b] = generate_int_and_ee2(ca_key, ca_cert,
-                                                     "limited-depth",
-                                                     CA_limited_basic_constraints,
-                                                     ee_ext_text);
-    #and a child using this one
-    generate_int_and_ee2(int_key, int_cert, "limited-depth-invalid",
-                         CA_basic_constraints, ee_ext_text);
-
-    # now we do it again for valid basic constraints but strange eku/ku at the
-    # intermediate layer
-    generate_int_and_ee2(ca_key, ca_cert, "valid-ku-no-eku",
-                         CA_basic_constraints + CA_min_ku, ee_ext_text);
-    generate_int_and_ee2(ca_key, ca_cert, "bad-ku-no-eku",
-                         CA_basic_constraints + CA_bad_ku, ee_ext_text);
-    generate_int_and_ee2(ca_key, ca_cert, "no-ku-no-eku",
-                         CA_basic_constraints, ee_ext_text);
-
-    generate_int_and_ee2(ca_key, ca_cert, "valid-ku-server-eku",
-                         CA_basic_constraints +  CA_min_ku + Server_eku,
-                         ee_ext_text);
-    generate_int_and_ee2(ca_key, ca_cert, "bad-ku-server-eku",
-                         CA_basic_constraints +  CA_bad_ku + Server_eku,
-                         ee_ext_text);
-    generate_int_and_ee2(ca_key, ca_cert, "no-ku-server-eku",
-                         CA_basic_constraints  + Server_eku, ee_ext_text);
-
-
-generate_certs()
-
-
deleted file mode 100644
index 742ad2ca5f414a56be072f8f0375e9b4577e8d93..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-bad-ku-no-eku
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,cRLSign
deleted file mode 100644
index 076202069f9267e17506037a452eaf125ada890d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-bad-ku-server-eku
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,cRLSign
+extension:extKeyUsage:serverAuth,clientAuth
deleted file mode 100644
index 89dfb986dc6bed0db0502bc22de8f473b896c268..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-cA-FALSE-asserts-keyCertSign
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
deleted file mode 100644
index 23334ace1f6decc16929553b193f4a05637d7e60..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-limited-depth
+subject:int-limited-depth-invalid
+extension:basicConstraints:cA,
deleted file mode 100644
index 415d5080c7172f65f5864e61c672e81afa687e49..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:int-limited-depth
+extension:basicConstraints:cA,0
deleted file mode 100644
index 78285ca6475dbbe104bf1637f5ac39a0a58f03f4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:int-no-extensions
deleted file mode 100644
index 1442b6f6c6e601690635481e15a5f496710da7f5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:int-no-ku-no-eku
+extension:basicConstraints:cA,
deleted file mode 100644
index 3be9fd6b992102adeced223485cc257b9f99d157..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-no-ku-server-eku
+extension:basicConstraints:cA,
+extension:extKeyUsage:serverAuth,clientAuth
deleted file mode 100644
index bc82a3492dc47c8b92a5e4f8902e25354a2b1e1f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:int-not-a-ca
+extension:basicConstraints:,
deleted file mode 100644
index 209ab602455c85d63ca22368d0c1633a38ecdb6a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-valid-ku-no-eku
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign
deleted file mode 100644
index aa3f080485f0d20679e92eb350473c3bf471f3ee..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-valid-ku-server-eku
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign
+extension:extKeyUsage:serverAuth,clientAuth
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build
@@ -0,0 +1,39 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+test_certificates = (
+    'ca.pem',
+    'ee-int-bad-ku-no-eku.pem',
+    'ee-int-bad-ku-server-eku.pem',
+    'ee-int-cA-FALSE-asserts-keyCertSign.pem',
+    'ee-int-limited-depth.pem',
+    'ee-int-limited-depth-invalid.pem',
+    'ee-int-no-extensions.pem',
+    'ee-int-no-ku-no-eku.pem',
+    'ee-int-no-ku-server-eku.pem',
+    'ee-int-not-a-ca.pem',
+    'ee-int-valid-ku-no-eku.pem',
+    'ee-int-valid-ku-server-eku.pem',
+    'int-bad-ku-no-eku.pem',
+    'int-bad-ku-server-eku.pem',
+    'int-cA-FALSE-asserts-keyCertSign.pem',
+    'int-limited-depth.pem',
+    'int-limited-depth-invalid.pem',
+    'int-no-extensions.pem',
+    'int-no-ku-no-eku.pem',
+    'int-no-ku-server-eku.pem',
+    'int-not-a-ca.pem',
+    'int-valid-ku-no-eku.pem',
+    'int-valid-ku-server-eku.pem',
+)
+
+for test_certificate in test_certificates:
+    input_file = test_certificate + '.certspec'
+    GENERATED_FILES += [test_certificate]
+    props = GENERATED_FILES[test_certificate]
+    props.script = '../pycert.py'
+    props.inputs = [input_file, TOPOBJDIR + '/config']
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_intermediate_basic_usage_constraints += ['!%s' % test_certificate]