mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1228794 - Convert test_getchain.js to generate certificates at build time. r=keeler
With this change, CertUtils.py is no longer needed.
This commit is contained in:
parent
aea0abf4da
commit
48152475d6
@ -16,6 +16,7 @@ TEST_DIRS += [
|
|||||||
'test_cert_trust',
|
'test_cert_trust',
|
||||||
'test_cert_version',
|
'test_cert_version',
|
||||||
'test_ev_certs',
|
'test_ev_certs',
|
||||||
|
'test_getchain',
|
||||||
'test_intermediate_basic_usage_constraints',
|
'test_intermediate_basic_usage_constraints',
|
||||||
'test_keysize',
|
'test_keysize',
|
||||||
'test_keysize_ev',
|
'test_keysize_ev',
|
||||||
|
@ -1,344 +0,0 @@
|
|||||||
# 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/.
|
|
||||||
|
|
||||||
# This file requires openssl 1.0.0 at least
|
|
||||||
|
|
||||||
import os
|
|
||||||
import random
|
|
||||||
import pexpect
|
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
|
|
||||||
default_validity_in_days = 10 * 365
|
|
||||||
|
|
||||||
def generate_cert_generic(db_dir, dest_dir, serial_num, key_type, name,
|
|
||||||
ext_text, signer_key_filename = "",
|
|
||||||
signer_cert_filename = "",
|
|
||||||
subject_string = "",
|
|
||||||
key_size = '2048',
|
|
||||||
validity_in_days = default_validity_in_days):
|
|
||||||
"""
|
|
||||||
Generate an x509 certificate with a sha256 signature
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
db_dir -- location of the temporary params for the certificate
|
|
||||||
dest_dir -- location of the x509 cert
|
|
||||||
serial_num -- serial number for the cert (must be unique for each signer
|
|
||||||
key)
|
|
||||||
key_type -- the type of key generated: potential values: 'rsa' or any
|
|
||||||
of the curves found by 'openssl ecparam -list_curves'
|
|
||||||
name -- the common name for the cert, will match the prefix of the
|
|
||||||
output cert
|
|
||||||
ext_text -- the text for the x509 extensions to be added to the
|
|
||||||
certificate
|
|
||||||
signer_key_filename -- the filename of the key from which the cert will
|
|
||||||
be signed if null the cert will be self signed (think CA
|
|
||||||
roots).
|
|
||||||
signer_cert_filename -- the certificate that will sign the certificate
|
|
||||||
(used to extract signer info) it must be in DER format.
|
|
||||||
key_size -- public key size for RSA certs
|
|
||||||
validity_in_days -- the number of days the cert will be valid for
|
|
||||||
|
|
||||||
output:
|
|
||||||
key_name -- the filename of the key file (PEM format)
|
|
||||||
cert_name -- the filename of the output certificate (DER format)
|
|
||||||
"""
|
|
||||||
key_name = db_dir + "/"+ name + ".key"
|
|
||||||
if key_type == 'rsa':
|
|
||||||
os.system ("openssl genpkey -algorithm RSA -out " + key_name +
|
|
||||||
" -pkeyopt rsa_keygen_bits:" + key_size)
|
|
||||||
else:
|
|
||||||
#assume is ec
|
|
||||||
os.system("openssl ecparam -out " + key_name + " -name "+ key_type +
|
|
||||||
" -genkey");
|
|
||||||
csr_name = db_dir + "/"+ name + ".csr"
|
|
||||||
if not subject_string:
|
|
||||||
subject_string = '/CN=' + name
|
|
||||||
os.system ("openssl req -new -key " + key_name + " -days 3650" +
|
|
||||||
" -extensions v3_ca -batch -out " + csr_name +
|
|
||||||
" -utf8 -subj '" + subject_string + "'")
|
|
||||||
|
|
||||||
extensions_filename = db_dir + "/openssl-exts"
|
|
||||||
f = open(extensions_filename,'w')
|
|
||||||
f.write(ext_text)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
cert_name = dest_dir + "/"+ name + ".der"
|
|
||||||
if not signer_key_filename:
|
|
||||||
signer_key_filename = key_name;
|
|
||||||
os.system ("openssl x509 -req -sha256 -in " + csr_name +
|
|
||||||
" -days " + str(validity_in_days) +
|
|
||||||
" -signkey " + signer_key_filename +
|
|
||||||
" -set_serial " + str(serial_num) +
|
|
||||||
" -extfile " + extensions_filename +
|
|
||||||
" -outform DER -out "+ cert_name)
|
|
||||||
else:
|
|
||||||
os.system ("openssl x509 -req -sha256 -in " + csr_name +
|
|
||||||
" -CAkey " + signer_key_filename +
|
|
||||||
" -CA " + signer_cert_filename + " -CAform DER " +
|
|
||||||
" -days " + str(validity_in_days) +
|
|
||||||
" -set_serial " + str(serial_num) + " -out " + cert_name +
|
|
||||||
" -outform DER -extfile " + extensions_filename)
|
|
||||||
return key_name, cert_name
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_int_and_ee(db_dir, dest_dir, ca_key, ca_cert, name, int_ext_text,
|
|
||||||
ee_ext_text, key_type = 'rsa',
|
|
||||||
ee_validity_in_days = default_validity_in_days):
|
|
||||||
"""
|
|
||||||
Generate an intermediate and ee signed by the generated intermediate. The
|
|
||||||
name of the intermediate files will be the name '.der' or '.key'. The name
|
|
||||||
of the end entity files with be "ee-"+ name plus the appropiate prefixes.
|
|
||||||
The serial number will be generated radomly so it is potentially possible
|
|
||||||
to have problem (but very unlikely).
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
db_dir -- location of the temporary params for the certificate
|
|
||||||
dest_dir -- location of the x509 cert
|
|
||||||
ca_key -- The filename of the key that will be used to sign the
|
|
||||||
intermediate (PEM FORMAT)
|
|
||||||
ca_cert -- The filename of the cert that will be used to sign the
|
|
||||||
intermediate, it MUST be the private key for the ca_key.
|
|
||||||
The file must be in DER format.
|
|
||||||
name -- the common name for the intermediate, will match the prefix
|
|
||||||
of the output intermediate. The ee will have the name
|
|
||||||
prefixed with "ee-"
|
|
||||||
int_ext_text -- the text for the x509 extensions to be added to the
|
|
||||||
intermediate certificate
|
|
||||||
ee_ext_text -- the text for the x509 extensions to be added to the
|
|
||||||
end entity certificate
|
|
||||||
key_type -- the type of key generated: potential values: 'rsa' or any
|
|
||||||
of the curves found by 'openssl ecparam -list_curves'
|
|
||||||
ee_validity_in_days -- the number of days the end-entity cert will be
|
|
||||||
valid for
|
|
||||||
|
|
||||||
output:
|
|
||||||
int_key -- the filename of the intermeidate key file (PEM format)
|
|
||||||
int_cert -- the filename of the intermediate certificate (DER format)
|
|
||||||
ee_key -- the filename of the end entity key file (PEM format)
|
|
||||||
ee_cert -- the filename of the end entity certficate (DER format)
|
|
||||||
|
|
||||||
"""
|
|
||||||
[int_key, int_cert] = generate_cert_generic(db_dir, dest_dir,
|
|
||||||
random.randint(100,40000000),
|
|
||||||
key_type, "int-" + name,
|
|
||||||
int_ext_text,
|
|
||||||
ca_key, ca_cert)
|
|
||||||
[ee_key, ee_cert] = generate_cert_generic(
|
|
||||||
db_dir,
|
|
||||||
dest_dir,
|
|
||||||
random.randint(100,40000000),
|
|
||||||
key_type,
|
|
||||||
name,
|
|
||||||
ee_ext_text,
|
|
||||||
int_key,
|
|
||||||
int_cert,
|
|
||||||
validity_in_days = ee_validity_in_days)
|
|
||||||
|
|
||||||
return int_key, int_cert, ee_key, ee_cert
|
|
||||||
|
|
||||||
def generate_pkcs12(db_dir, dest_dir, der_cert_filename, key_pem_filename,
|
|
||||||
prefix):
|
|
||||||
"""
|
|
||||||
Generate a pkcs12 file for a given certificate name (in der format) and
|
|
||||||
a key filename (key in pem format). The output file will have an empty
|
|
||||||
password.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
input:
|
|
||||||
db_dir -- location of the temporary params for the certificate
|
|
||||||
dest_dir -- location of the x509 cert
|
|
||||||
der_cert_filename -- the filename of the certificate to be included in the
|
|
||||||
output file (DER format)
|
|
||||||
key_pem_filename -- the filename of the private key of the certificate to
|
|
||||||
(PEM format)
|
|
||||||
prefix -- the name to be prepended to the output pkcs12 file.
|
|
||||||
output:
|
|
||||||
pk12_filename -- the filename of the outgoing pkcs12 output file
|
|
||||||
"""
|
|
||||||
#make pem cert file from der filename
|
|
||||||
pem_cert_filename = db_dir + "/" + prefix + ".pem"
|
|
||||||
pk12_filename = dest_dir + "/" + prefix + ".p12"
|
|
||||||
os.system("openssl x509 -inform der -in " + der_cert_filename + " -out " +
|
|
||||||
pem_cert_filename )
|
|
||||||
#now make pkcs12 file
|
|
||||||
child = pexpect.spawn("openssl pkcs12 -export -in " + pem_cert_filename +
|
|
||||||
" -inkey " + key_pem_filename + " -out " +
|
|
||||||
pk12_filename)
|
|
||||||
child.expect('Enter Export Password:')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect('Verifying - Enter Export Password:')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect(pexpect.EOF)
|
|
||||||
return pk12_filename
|
|
||||||
|
|
||||||
def print_cert_info(cert_filename):
|
|
||||||
"""
|
|
||||||
Prints out information (such as fingerprints) for the given cert.
|
|
||||||
The information printed is sufficient for enabling EV for the given cert
|
|
||||||
if necessary.
|
|
||||||
|
|
||||||
Note: The utility 'pp' is available as part of NSS.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
cert_filename -- the filename of the cert in DER format
|
|
||||||
"""
|
|
||||||
os.system('pp -t certificate-identity -i ' + cert_filename)
|
|
||||||
|
|
||||||
def init_nss_db(db_dir):
|
|
||||||
"""
|
|
||||||
Remove the current nss database in the specified directory and create a new
|
|
||||||
nss database with the sql format.
|
|
||||||
Arguments
|
|
||||||
db_dir -- the desired location of the new database
|
|
||||||
output
|
|
||||||
noise_file -- the path to a noise file suitable to generate TEST
|
|
||||||
certificates. This does not have enough entropy for a real
|
|
||||||
secret
|
|
||||||
pwd_file -- the patch to the secret file used for the database.
|
|
||||||
this file should be empty.
|
|
||||||
"""
|
|
||||||
nss_db_files = ["cert9.db", "key4.db", "pkcs11.txt"]
|
|
||||||
for file in nss_db_files:
|
|
||||||
if os.path.isfile(file):
|
|
||||||
os.remove(file)
|
|
||||||
# create noise file
|
|
||||||
noise_file = db_dir + "/noise"
|
|
||||||
nf = open(noise_file, 'w')
|
|
||||||
nf.write(str(time.time()))
|
|
||||||
nf.close()
|
|
||||||
# create pwd file
|
|
||||||
pwd_file = db_dir + "/pwfile"
|
|
||||||
pf = open(pwd_file, 'w')
|
|
||||||
pf.write("\n")
|
|
||||||
pf.close()
|
|
||||||
# create nss db
|
|
||||||
os.system("certutil -d sql:" + db_dir + " -N -f " + pwd_file);
|
|
||||||
return [noise_file, pwd_file]
|
|
||||||
|
|
||||||
def generate_self_signed_cert(db_dir, dest_dir, noise_file, name, version, do_bc, is_ca):
|
|
||||||
"""
|
|
||||||
Creates a new self-signed certificate in an sql NSS database and as a der file
|
|
||||||
Arguments:
|
|
||||||
db_dir -- the location of the nss database (in sql format)
|
|
||||||
dest_dir -- the location of for the output file
|
|
||||||
noise_file -- the location of a noise file.
|
|
||||||
name -- the nickname of the new certificate in the database and the
|
|
||||||
common name of the certificate
|
|
||||||
version -- the version number of the certificate (valid certs must use
|
|
||||||
3)
|
|
||||||
do_bc -- if the certificate should include the basic constraints
|
|
||||||
(valid ca's should be true)
|
|
||||||
is_ca -- mark the extenstion true or false
|
|
||||||
output:
|
|
||||||
outname -- the location of the der file.
|
|
||||||
"""
|
|
||||||
out_name = dest_dir + "/" + name + ".der"
|
|
||||||
base_exec_string = ("certutil -S -z " + noise_file + " -g 2048 -d sql:" +
|
|
||||||
db_dir + "/ -n " + name + " -v 120 -s 'CN=" + name +
|
|
||||||
",O=PSM Testing,L=Mountain View,ST=California,C=US'" +
|
|
||||||
" -t C,C,C -x --certVersion=" + str(int(version)))
|
|
||||||
if (do_bc):
|
|
||||||
child = pexpect.spawn(base_exec_string + " -2")
|
|
||||||
child.logfile = sys.stdout
|
|
||||||
child.expect('Is this a CA certificate \[y/N\]?')
|
|
||||||
if (is_ca):
|
|
||||||
child.sendline('y')
|
|
||||||
else:
|
|
||||||
child.sendline('N')
|
|
||||||
child.expect('Enter the path length constraint, enter to skip \[<0 for unlimited path\]: >')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect('Is this a critical extension \[y/N\]?')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect(pexpect.EOF)
|
|
||||||
else:
|
|
||||||
os.system(base_exec_string)
|
|
||||||
os.system("certutil -d sql:" + db_dir + "/ -L -n " + name + " -r > " +
|
|
||||||
out_name)
|
|
||||||
return out_name
|
|
||||||
|
|
||||||
def generate_ca_cert(db_dir, dest_dir, noise_file, name, version, do_bc):
|
|
||||||
"""
|
|
||||||
Creates a new CA certificate in an sql NSS database and as a der file
|
|
||||||
Arguments:
|
|
||||||
db_dir -- the location of the nss database (in sql format)
|
|
||||||
dest_dir -- the location of for the output file
|
|
||||||
noise_file -- the location of a noise file.
|
|
||||||
name -- the nickname of the new certificate in the database and the
|
|
||||||
common name of the certificate
|
|
||||||
version -- the version number of the certificate (valid certs must use
|
|
||||||
3)
|
|
||||||
do_bc -- if the certificate should include the basic constraints
|
|
||||||
(valid ca's should be true)
|
|
||||||
output:
|
|
||||||
outname -- the location of the der file.
|
|
||||||
"""
|
|
||||||
return generate_self_signed_cert(db_dir, dest_dir, noise_file, name, version, do_bc, True)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_child_cert(db_dir, dest_dir, noise_file, name, ca_nick, version,
|
|
||||||
do_bc, is_ee, ocsp_url):
|
|
||||||
"""
|
|
||||||
Creates a new child certificate in an sql NSS database and as a der file
|
|
||||||
Arguments:
|
|
||||||
db_dir -- the location of the nss database (in sql format)
|
|
||||||
dest_dir -- the location of for the output file
|
|
||||||
noise_file -- the location of a noise file.
|
|
||||||
name -- the nickname of the new certificate in the database and the
|
|
||||||
common name of the certificate
|
|
||||||
ca_nick -- the nickname of the isser of the new certificate
|
|
||||||
version -- the version number of the certificate (valid certs must use
|
|
||||||
3)
|
|
||||||
do_bc -- if the certificate should include the basic constraints
|
|
||||||
(valid ca's should be true)
|
|
||||||
is_ee -- is this and End Entity cert? false means intermediate
|
|
||||||
ocsp_url -- optional location of the ocsp responder for this certificate
|
|
||||||
this is included only if do_bc is set to True
|
|
||||||
output:
|
|
||||||
outname -- the location of the der file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
out_name = dest_dir + "/" + name + ".der"
|
|
||||||
base_exec_string = ("certutil -S -z " + noise_file + " -g 2048 -d sql:" +
|
|
||||||
db_dir + "/ -n " + name + " -v 120 -m " +
|
|
||||||
str(random.randint(100, 40000000)) + " -s 'CN=" + name +
|
|
||||||
",O=PSM Testing,L=Mountain View,ST=California,C=US'" +
|
|
||||||
" -t C,C,C -c " + ca_nick + " --certVersion=" +
|
|
||||||
str(int(version)))
|
|
||||||
if (do_bc):
|
|
||||||
extra_arguments = " -2"
|
|
||||||
if (ocsp_url):
|
|
||||||
extra_arguments += " --extAIA"
|
|
||||||
child = pexpect.spawn(base_exec_string + extra_arguments)
|
|
||||||
child.logfile = sys.stdout
|
|
||||||
child.expect('Is this a CA certificate \[y/N\]?')
|
|
||||||
if (is_ee):
|
|
||||||
child.sendline('N')
|
|
||||||
else:
|
|
||||||
child.sendline('y')
|
|
||||||
child.expect('Enter the path length constraint, enter to skip \[<0 for unlimited path\]: >')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect('Is this a critical extension \[y/N\]?')
|
|
||||||
child.sendline('')
|
|
||||||
if (ocsp_url):
|
|
||||||
child.expect('\s+Choice >')
|
|
||||||
child.sendline('2')
|
|
||||||
child.expect('\s+Choice: >')
|
|
||||||
child.sendline('7')
|
|
||||||
child.expect('Enter data:')
|
|
||||||
child.sendline(ocsp_url)
|
|
||||||
child.expect('\s+Choice: >')
|
|
||||||
child.sendline('0')
|
|
||||||
child.expect('Add another location to the Authority Information Access extension \[y/N\]')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect('Is this a critical extension \[y/N\]?')
|
|
||||||
child.sendline('')
|
|
||||||
child.expect(pexpect.EOF)
|
|
||||||
else:
|
|
||||||
os.system(base_exec_string)
|
|
||||||
os.system("certutil -d sql:" + db_dir + "/ -L -n " + name + " -r > " +
|
|
||||||
out_name)
|
|
||||||
return out_name
|
|
||||||
|
|
@ -18,6 +18,7 @@ subject:<subject distinguished name specification>
|
|||||||
[subjectKey:<key specification>]
|
[subjectKey:<key specification>]
|
||||||
[signature:{sha256WithRSAEncryption,sha1WithRSAEncryption,
|
[signature:{sha256WithRSAEncryption,sha1WithRSAEncryption,
|
||||||
md5WithRSAEncryption,ecdsaWithSHA256}]
|
md5WithRSAEncryption,ecdsaWithSHA256}]
|
||||||
|
[serialNumber:<integer in the interval [1, 127]>]
|
||||||
[extension:<extension name:<extension-specific data>>]
|
[extension:<extension name:<extension-specific data>>]
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
@ -72,8 +73,11 @@ If an extension name has '[critical]' after it, it will be marked as
|
|||||||
critical. Otherwise (by default), it will not be marked as critical.
|
critical. Otherwise (by default), it will not be marked as critical.
|
||||||
|
|
||||||
TLSFeature values can either consist of a named value (currently only
|
TLSFeature values can either consist of a named value (currently only
|
||||||
'OCSPMustStaple' which corresponds to status_request) or a numeric tls feature
|
'OCSPMustStaple' which corresponds to status_request) or a numeric TLS
|
||||||
value (see rfc7633 for more information).
|
feature value (see rfc7633 for more information).
|
||||||
|
|
||||||
|
If a serial number is not explicitly specified, it is automatically
|
||||||
|
generated based on the contents of the certificate.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyasn1.codec.der import decoder
|
from pyasn1.codec.der import decoder
|
||||||
@ -113,7 +117,12 @@ class NameConstraints(univ.Sequence):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UnknownBaseError(Exception):
|
class Error(Exception):
|
||||||
|
"""Base class for exceptions in this module."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownBaseError(Error):
|
||||||
"""Base class for handling unexpected input in this module."""
|
"""Base class for handling unexpected input in this module."""
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
super(UnknownBaseError, self).__init__()
|
super(UnknownBaseError, self).__init__()
|
||||||
@ -196,6 +205,7 @@ class UnknownNSCertTypeError(UnknownBaseError):
|
|||||||
UnknownBaseError.__init__(self, value)
|
UnknownBaseError.__init__(self, value)
|
||||||
self.category = 'nsCertType'
|
self.category = 'nsCertType'
|
||||||
|
|
||||||
|
|
||||||
class UnknownTLSFeature(UnknownBaseError):
|
class UnknownTLSFeature(UnknownBaseError):
|
||||||
"""Helper exception type to handle unknown TLS Features."""
|
"""Helper exception type to handle unknown TLS Features."""
|
||||||
|
|
||||||
@ -204,6 +214,17 @@ class UnknownTLSFeature(UnknownBaseError):
|
|||||||
self.category = 'TLSFeature'
|
self.category = 'TLSFeature'
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidSerialNumber(Error):
|
||||||
|
"""Exception type to handle invalid serial numbers."""
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
super(InvalidSerialNumber, self).__init__()
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
|
|
||||||
def getASN1Tag(asn1Type):
|
def getASN1Tag(asn1Type):
|
||||||
"""Helper function for returning the base tag value of a given
|
"""Helper function for returning the base tag value of a given
|
||||||
type from the pyasn1 package"""
|
type from the pyasn1 package"""
|
||||||
@ -309,6 +330,16 @@ def datetimeToTime(dt):
|
|||||||
time.setComponentByName('generalTime', useful.GeneralizedTime(dt.strftime('%Y%m%d%H%M%SZ')))
|
time.setComponentByName('generalTime', useful.GeneralizedTime(dt.strftime('%Y%m%d%H%M%SZ')))
|
||||||
return time
|
return time
|
||||||
|
|
||||||
|
def serialBytesToString(serialBytes):
|
||||||
|
"""Takes a list of integers in the interval [0, 255] and returns
|
||||||
|
the corresponding serial number string."""
|
||||||
|
serialBytesLen = len(serialBytes)
|
||||||
|
if serialBytesLen > 127:
|
||||||
|
raise InvalidSerialNumber("{} bytes is too long".format(serialBytesLen))
|
||||||
|
# Prepend the ASN.1 INTEGER tag and length bytes.
|
||||||
|
stringBytes = [getASN1Tag(univ.Integer), serialBytesLen] + serialBytes
|
||||||
|
return ''.join(chr(b) for b in stringBytes)
|
||||||
|
|
||||||
class Certificate(object):
|
class Certificate(object):
|
||||||
"""Utility class for reading a certificate specification and
|
"""Utility class for reading a certificate specification and
|
||||||
generating a signed x509 certificate"""
|
generating a signed x509 certificate"""
|
||||||
@ -326,8 +357,12 @@ class Certificate(object):
|
|||||||
self.extensions = None
|
self.extensions = None
|
||||||
self.subjectKey = pykey.keyFromSpecification('default')
|
self.subjectKey = pykey.keyFromSpecification('default')
|
||||||
self.issuerKey = pykey.keyFromSpecification('default')
|
self.issuerKey = pykey.keyFromSpecification('default')
|
||||||
|
self.serialNumber = None
|
||||||
self.decodeParams(paramStream)
|
self.decodeParams(paramStream)
|
||||||
self.serialNumber = self.generateSerialNumber()
|
# If a serial number wasn't specified, generate one based on
|
||||||
|
# the certificate contents.
|
||||||
|
if not self.serialNumber:
|
||||||
|
self.serialNumber = self.generateSerialNumber()
|
||||||
|
|
||||||
def generateSerialNumber(self):
|
def generateSerialNumber(self):
|
||||||
"""Generates a serial number for this certificate based on its
|
"""Generates a serial number for this certificate based on its
|
||||||
@ -353,10 +388,7 @@ class Certificate(object):
|
|||||||
# significant byte is set (to prevent a leading zero byte,
|
# significant byte is set (to prevent a leading zero byte,
|
||||||
# which also wouldn't be valid).
|
# which also wouldn't be valid).
|
||||||
serialBytes[0] |= 0x01
|
serialBytes[0] |= 0x01
|
||||||
# Now prepend the ASN.1 INTEGER tag and length bytes.
|
return serialBytesToString(serialBytes)
|
||||||
serialBytes.insert(0, len(serialBytes))
|
|
||||||
serialBytes.insert(0, getASN1Tag(univ.Integer))
|
|
||||||
return ''.join(chr(b) for b in serialBytes)
|
|
||||||
|
|
||||||
def decodeParams(self, paramStream):
|
def decodeParams(self, paramStream):
|
||||||
for line in paramStream.readlines():
|
for line in paramStream.readlines():
|
||||||
@ -381,6 +413,13 @@ class Certificate(object):
|
|||||||
self.setupKey('subject', value)
|
self.setupKey('subject', value)
|
||||||
elif param == 'signature':
|
elif param == 'signature':
|
||||||
self.signature = value
|
self.signature = value
|
||||||
|
elif param == 'serialNumber':
|
||||||
|
serialNumber = int(value)
|
||||||
|
# Ensure only serial numbers that conform to the rules listed in
|
||||||
|
# generateSerialNumber() are permitted.
|
||||||
|
if serialNumber < 1 or serialNumber > 127:
|
||||||
|
raise InvalidSerialNumber(value)
|
||||||
|
self.serialNumber = serialBytesToString([serialNumber])
|
||||||
else:
|
else:
|
||||||
raise UnknownParameterTypeError(param)
|
raise UnknownParameterTypeError(param)
|
||||||
|
|
||||||
@ -553,13 +592,12 @@ class Certificate(object):
|
|||||||
def addTLSFeature(self, features, critical):
|
def addTLSFeature(self, features, critical):
|
||||||
namedFeatures = {'OCSPMustStaple': 5}
|
namedFeatures = {'OCSPMustStaple': 5}
|
||||||
featureList = [f.strip() for f in features.split(',')]
|
featureList = [f.strip() for f in features.split(',')]
|
||||||
print "FeatureList is",featureList
|
|
||||||
sequence = univ.Sequence()
|
sequence = univ.Sequence()
|
||||||
for feature in featureList:
|
for feature in featureList:
|
||||||
featureValue = 0
|
featureValue = 0
|
||||||
try:
|
try:
|
||||||
featureValue = int(feature)
|
featureValue = int(feature)
|
||||||
except:
|
except ValueError:
|
||||||
try:
|
try:
|
||||||
featureValue = namedFeatures[feature]
|
featureValue = namedFeatures[feature]
|
||||||
except:
|
except:
|
||||||
|
@ -8,19 +8,13 @@
|
|||||||
do_get_profile(); // must be called before getting nsIX509CertDB
|
do_get_profile(); // must be called before getting nsIX509CertDB
|
||||||
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
||||||
.getService(Ci.nsIX509CertDB);
|
.getService(Ci.nsIX509CertDB);
|
||||||
// This is the list of certificates needed for the test
|
// This is the list of certificates needed for the test.
|
||||||
// The certificates prefixed by 'int-' are intermediates
|
|
||||||
var certList = [
|
var certList = [
|
||||||
'ee',
|
'ee',
|
||||||
'ca-1',
|
'ca-1',
|
||||||
'ca-2',
|
'ca-2',
|
||||||
];
|
];
|
||||||
|
|
||||||
function load_cert(cert_name, trust_string) {
|
|
||||||
var cert_filename = cert_name + ".der";
|
|
||||||
addCertFromFile(certdb, "test_getchain/" + cert_filename, trust_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since all the ca's are identical expect for the serial number
|
// Since all the ca's are identical expect for the serial number
|
||||||
// I have to grab them by enumerating all the certs and then finding
|
// I have to grab them by enumerating all the certs and then finding
|
||||||
// the ones that I am interested in.
|
// the ones that I am interested in.
|
||||||
@ -31,7 +25,7 @@ function get_ca_array() {
|
|||||||
while (enumerator.hasMoreElements()) {
|
while (enumerator.hasMoreElements()) {
|
||||||
let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
|
let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
|
||||||
if (cert.commonName == 'ca') {
|
if (cert.commonName == 'ca') {
|
||||||
ret_array[parseInt(cert.serialNumber)] = cert;
|
ret_array[parseInt(cert.serialNumber, 16)] = cert;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret_array;
|
return ret_array;
|
||||||
@ -53,7 +47,7 @@ function check_matching_issuer_and_getchain(expected_issuer_serial, cert) {
|
|||||||
function check_getchain(ee_cert, ssl_ca, email_ca){
|
function check_getchain(ee_cert, ssl_ca, email_ca){
|
||||||
// A certificate should first build a chain/issuer to
|
// A certificate should first build a chain/issuer to
|
||||||
// a SSL trust domain, then an EMAIL trust domain and then
|
// a SSL trust domain, then an EMAIL trust domain and then
|
||||||
// and object signer trust domain
|
// an object signer trust domain.
|
||||||
|
|
||||||
const nsIX509Cert = Components.interfaces.nsIX509Cert;
|
const nsIX509Cert = Components.interfaces.nsIX509Cert;
|
||||||
certdb.setCertTrust(ssl_ca, nsIX509Cert.CA_CERT,
|
certdb.setCertTrust(ssl_ca, nsIX509Cert.CA_CERT,
|
||||||
@ -73,8 +67,8 @@ function run_test() {
|
|||||||
clearOCSPCache();
|
clearOCSPCache();
|
||||||
clearSessionCache();
|
clearSessionCache();
|
||||||
|
|
||||||
for (let i = 0 ; i < certList.length; i++) {
|
for (let cert of certList) {
|
||||||
load_cert(certList[i], ',,');
|
addCertFromFile(certdb, `test_getchain/${cert}.pem`, ",,");
|
||||||
}
|
}
|
||||||
|
|
||||||
let ee_cert = certdb.findCertByNickname(null, 'ee');
|
let ee_cert = certdb.findCertByNickname(null, 'ee');
|
||||||
|
Binary file not shown.
@ -0,0 +1,5 @@
|
|||||||
|
issuer:ca
|
||||||
|
subject:ca
|
||||||
|
extension:basicConstraints:cA,
|
||||||
|
extension:keyUsage:cRLSign,keyCertSign
|
||||||
|
serialNumber:1
|
Binary file not shown.
@ -0,0 +1,5 @@
|
|||||||
|
issuer:ca
|
||||||
|
subject:ca
|
||||||
|
extension:basicConstraints:cA,
|
||||||
|
extension:keyUsage:cRLSign,keyCertSign
|
||||||
|
serialNumber:2
|
Binary file not shown.
@ -0,0 +1,2 @@
|
|||||||
|
issuer:ca
|
||||||
|
subject:ee
|
@ -1,73 +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/.
|
|
||||||
|
|
||||||
import tempfile, os, sys
|
|
||||||
import random
|
|
||||||
import pexpect
|
|
||||||
import subprocess
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
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"
|
|
||||||
EE_basic_constraints = "basicConstraints = CA:FALSE\n"
|
|
||||||
|
|
||||||
CA_full_ku = ("keyUsage = digitalSignature, nonRepudiation, keyEncipherment, " +
|
|
||||||
"dataEncipherment, keyAgreement, keyCertSign, cRLSign\n")
|
|
||||||
|
|
||||||
CA_eku = ("extendedKeyUsage = critical, serverAuth, clientAuth, " +
|
|
||||||
"emailProtection, codeSigning\n")
|
|
||||||
|
|
||||||
authority_key_ident = "authorityKeyIdentifier = keyid, issuer\n"
|
|
||||||
subject_key_ident = "subjectKeyIdentifier = hash\n"
|
|
||||||
|
|
||||||
|
|
||||||
def self_sign_csr(db_dir, dst_dir, csr_name, key_file, serial_num, ext_text,
|
|
||||||
out_prefix):
|
|
||||||
extensions_filename = db_dir + "/openssl-exts"
|
|
||||||
f = open(extensions_filename, 'w')
|
|
||||||
f.write(ext_text)
|
|
||||||
f.close()
|
|
||||||
cert_name = dst_dir + "/" + out_prefix + ".der"
|
|
||||||
os.system ("openssl x509 -req -sha256 -days 3650 -in " + csr_name +
|
|
||||||
" -signkey " + key_file +
|
|
||||||
" -set_serial " + str(serial_num) +
|
|
||||||
" -extfile " + extensions_filename +
|
|
||||||
" -outform DER -out " + cert_name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_certs():
|
|
||||||
key_type = 'rsa'
|
|
||||||
ca_ext = CA_basic_constraints + CA_full_ku + subject_key_ident + CA_eku;
|
|
||||||
ee_ext_text = (EE_basic_constraints + authority_key_ident)
|
|
||||||
[ca_key, ca_cert] = CertUtils.generate_cert_generic(db,
|
|
||||||
srcdir,
|
|
||||||
1,
|
|
||||||
key_type,
|
|
||||||
'ca',
|
|
||||||
ca_ext)
|
|
||||||
CertUtils.generate_cert_generic(db,
|
|
||||||
srcdir,
|
|
||||||
100,
|
|
||||||
key_type,
|
|
||||||
'ee',
|
|
||||||
ee_ext_text,
|
|
||||||
ca_key,
|
|
||||||
ca_cert)
|
|
||||||
|
|
||||||
shutil.copy(ca_cert, srcdir + "/" + "ca-1.der")
|
|
||||||
self_sign_csr(db, srcdir, db + "/ca.csr", ca_key, 2, ca_ext, "ca-2")
|
|
||||||
os.remove(ca_cert);
|
|
||||||
|
|
||||||
generate_certs()
|
|
19
security/manager/ssl/tests/unit/test_getchain/moz.build
Normal file
19
security/manager/ssl/tests/unit/test_getchain/moz.build
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- 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-1.pem',
|
||||||
|
'ca-2.pem',
|
||||||
|
'ee.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]
|
||||||
|
TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_getchain += ['!%s' % test_certificate]
|
Loading…
Reference in New Issue
Block a user