bug 1166976 - generate some PSM xpcshell test certificates at build time draft
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 20 May 2015 16:35:16 -0700
changeset 268057 d7d86058c9a1bdd0c5d9245285bbb52a2dfa7527
parent 268056 58b27b3f1fd2a2e3f38ece06acb24d1f1177e927
child 506442 018c5690fff67fa3f303a011698edd76f91e1e07
push id2301
push userdkeeler@mozilla.com
push dateWed, 27 May 2015 18:26:06 +0000
bugs1166976
milestone41.0a1
bug 1166976 - generate some PSM xpcshell test certificates at build time
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
@@ -19,11 +19,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-0.0.5
 bitstring.pth:python/bitstring
 redo.pth:python/redo
 requests.pth:python/requests
+rsa.pth:python/rsa-3.1.4
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -95,17 +95,28 @@ function readFile(file) {
   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 successful = false;
+  try {
+    certdb.addCert(der, trustString, null);
+    successful = true;
+  } catch (e) {}
+  if (!successful) {
+    // It might be PEM instead of DER.
+    let pem = der.replace(/-----BEGIN CERTIFICATE-----/, "")
+                 .replace(/-----END CERTIFICATE-----/, "")
+                 .replace(/[\r\n]/g, "");
+    certdb.addCertFromBase64(pem, 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,392 @@
+#!/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):
+        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):
+    buildid = open('%s/buildid' % buildIDPath).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,22 @@ 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))
+                   .replace(/-----BEGIN CERTIFICATE-----/, "")
+                   .replace(/-----END CERTIFICATE-----/, "")
+                   .replace(/[\r\n]/g, "");
+  let ee = certDB.constructX509FromBase64(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,42 @@
 "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)
+                    .replace(/-----BEGIN CERTIFICATE-----/, "")
+                    .replace(/-----END CERTIFICATE-----/, "")
+                    .replace(/[\r\n]/g, "");
+    certs.push(certdb.constructX509FromBase64(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 +67,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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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..0000000000000000000000000000000000000000
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]