On Fri, 3 Mar 2017, Robert Relyea wrote:
Yes, NSS only looks at the first subjectAltName section. That section can and
should hold all the alt names (That's certainly the case with SSL
certificates).
Ok, so maybe we have generated certificates in a bad way :)
See the attached python script that generates these :)
So there are 3 subjectAltName sections.
Hmm Are you sure these aren't being colapsed into a single subjectAltName
section with multiple subjectAltNames in it?
Yes because I was testing the IP: and DNS: and email= SAN's and my code
showed it only looped over the first one.
CERT_VerifyCertName() verifies against either the SAN or the CN (SSL
processing). it supports multiple subjectAltNames, but they all are expected to
be in the same section.
So it seems that this function would do everything I need. I'll see
about changing my certificates to have one SAN section and then
confirm if it can pick up the DNS name or email address from CN
and SAN.
Thanks,
Paul
#!/usr/bin/python
""" dist_certs.py: create a suite of x509 certificates for the Libreswan
test harness
Copyright (C) 2014-2015 Matt Rogers <mrog...@redhat.com>
Copyright (C) 2015 Andrew Cagney <andrew.cag...@gmail.com>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
WARNING! Your PyOpenSSL needs a patch from here:
https://github.com/pyca/pyopenssl/pull/161
NSS doesn't allow md5 CRL signatures. This patch lets you use
the CRL export method and specify an acceptable signature type.
"""
import os
import sys
import ssl
import shutil
import subprocess
import time
from datetime import datetime, timedelta
import pexpect
from OpenSSL import crypto
CRL_URI = 'URI:http://nic.testing.libreswan.org/revoked.crl'
dates = {}
ca_certs = {}
end_certs = {}
endrev_name = ""
top_caname=""
def reset_files():
for dir in ['keys/', 'cacerts/', 'certs/', 'pkcs12/',
'pkcs12/curveca', 'pkcs12/mainca',
'pkcs12/otherca', 'pkcs12/badca', 'crls/']:
if os.path.isdir(dir):
shutil.rmtree(dir)
os.mkdir(dir)
for file in ['nss-pw']:
if os.path.isfile(file):
os.remove(file)
def writeout_cert(filename, item,
type=crypto.FILETYPE_PEM):
with open(filename, "w") as f:
f.write(crypto.dump_certificate(type, item))
def writeout_privkey(filename, item,
type=crypto.FILETYPE_PEM):
with open(filename, "w") as f:
f.write(crypto.dump_privatekey(type, item))
def create_keypair(algo=crypto.TYPE_RSA, bits=1024):
""" Create an OpenSSL keypair
"""
pkey = crypto.PKey()
pkey.generate_key(algo, bits)
return pkey
def create_csr(pkey, CN,
C=None, ST=None, L=None, O=None, OU=None,
emailAddress=None, algo='sha1'):
""" Create the certreq
"""
req = crypto.X509Req()
subject = req.get_subject()
subject.CN = CN
subject.C = C
subject.ST = ST
subject.L = L
subject.O = O
subject.OU = OU
subject.CN = CN
subject.emailAddress = emailAddress
req.set_pubkey(pkey)
req.sign(pkey, algo)
return req
def add_ext(cert, kind, crit, string):
cert.add_extensions([crypto.X509Extension(kind, crit, string)])
def set_cert_extensions(cert, issuer, isCA=False, isRoot=False, ocsp=False,
ocspuri=True):
ku_str = 'digitalSignature'
eku_str = ''
ocspeku = 'serverAuth,clientAuth,codeSigning,OCSPSigning'
cnstr = str(cert.get_subject().commonName)
if isCA:
ku_str = ku_str + ',keyCertSign,cRLSign'
if "badca" in str(issuer.get_subject().commonName):
bc = "CA:FALSE"
else:
bc = "CA:TRUE"
else:
bc = "CA:FALSE"
add_ext(cert, 'basicConstraints', False, bc)
if not isCA:
dnsname = "DNS: " + cnstr
if cnstr == "east.testing.libreswan.org":
dnsname = "%s,%s"%(dnsname , "DNS:east.alias")
add_ext(cert, 'subjectAltName', False, "IP: 192.1.2.23")
add_ext(cert, 'subjectAltName', False, dnsname)
add_ext(cert, 'subjectAltName', False, "email:
user1@%s"%cnstr)
if cnstr == "west.testing.libreswan.org":
dnsname = "%s,%s"%(dnsname , "DNS:west.alias")
add_ext(cert, 'subjectAltName', False, "IP: 192.1.2.45")
add_ext(cert, 'subjectAltName', False, dnsname)
add_ext(cert, 'subjectAltName', False, "email:
user1@%s"%cnstr)
if cnstr == 'usage-server.testing.libreswan.org':
eku_str = 'serverAuth'
ku_str = ku_str + ',keyEncipherment'
elif cnstr == 'usage-client.testing.libreswan.org':
eku_str = 'clientAuth'
ku_str = ku_str + ',nonRepudiation'
elif cnstr == 'usage-both.testing.libreswan.org':
eku_str = 'serverAuth,clientAuth'
ku_str = ku_str + ',keyEncipherment,nonRepudiation'
if ocsp:
ku_str = ku_str + ',keyCertSign,cRLSign'
eku_str = ocspeku
add_ext(cert, 'keyUsage', False, ku_str)
if eku_str is not '':
add_ext(cert, 'extendedKeyUsage', False, eku_str)
if ocspuri:
add_ext(cert, 'authorityInfoAccess', False,
'OCSP;URI:http://nic.testing.libreswan.org:2560')
add_ext(cert, 'crlDistributionPoints', False, CRL_URI)
def create_sub_cert(CN, CACert, CAkey, snum, START, END,
C='CA', ST='Ontario', L='Toronto',
O='Libreswan', OU='Test Department',
emailAddress='test...@libreswan.org',
ty=crypto.TYPE_RSA, keybits=1024,
sign_alg='sha1', isCA=False,
ocsp=False):
""" Create a subordinate cert and return the cert, key tuple
This could be a CA for an intermediate, or not for an EE
"""
certkey = create_keypair(ty, keybits)
certreq = create_csr(certkey,
CN, C, ST, L, O, OU,
emailAddress, sign_alg)
cert = crypto.X509()
cert.set_serial_number(snum)
cert.set_notBefore(START)
cert.set_notAfter(END)
cert.set_issuer(CACert.get_subject())
cert.set_subject(certreq.get_subject())
cert.set_pubkey(certreq.get_pubkey())
cert.set_version(2)
if CN == 'nic-nourl.testing.libreswan.org':
ocspuri = False
else:
ocspuri = True
set_cert_extensions(cert, CACert, isCA=isCA, isRoot=False, ocsp=ocsp,
ocspuri=ocspuri)
cert.sign(CAkey, sign_alg)
return cert, certkey
def create_root_ca(CN, START, END,
C='CA', ST='Ontario', L='Toronto',
O='Libreswan', OU='Test Department',
emailAddress='test...@libreswan.org',
ty=crypto.TYPE_RSA, keybits=1024,
sign_alg='sha1'):
""" Create a root CA - Returns the cert, key tuple
"""
cakey = create_keypair(ty, keybits)
careq = create_csr(cakey, CN, C, ST, L, O, OU,
emailAddress, sign_alg)
cacert = crypto.X509()
cacert.set_serial_number(0)
cacert.set_notBefore(START)
cacert.set_notAfter(END)
cacert.set_issuer(careq.get_subject())
cacert.set_subject(careq.get_subject())
cacert.set_pubkey(careq.get_pubkey())
cacert.set_version(2)
set_cert_extensions(cacert, cacert, isCA=True, isRoot=True, ocsp=True,
ocspuri=True)
cacert.sign(cakey, sign_alg)
return cacert, cakey
def gmc(timestamp):
return time.strftime("%Y%m%d%H%M%SZ",
time.gmtime(timestamp))
def gen_gmtime_dates():
""" Generate the dates used for this run.
Creating openssl gmtime dates may be simpler than this.
"""
gmtfmt = "%b %d %H:%M:%S %Y GMT"
ok_stamp = ssl.cert_time_to_seconds(
time.strftime(gmtfmt, time.gmtime())) - (60*60*24)
two_days_ago_stamp = ok_stamp - (60*60*48)
two_days_ago_end_stamp = two_days_ago_stamp + (60*60*24)
# Make future certs only +300 days, so we have a time overlap
# between currently valid certs (1 year) and these futuristic certs
future_stamp = ok_stamp + (60*60*24*365*1)
future_end_stamp = future_stamp + (60*60*24*365*2)
return dict(OK_NOW=gmc(ok_stamp),
OLD=gmc(two_days_ago_stamp),
OLD_END=gmc(two_days_ago_end_stamp),
FUTURE=gmc(future_stamp),
FUTURE_END=gmc(future_end_stamp))
def store_cert_and_key(name, cert, key):
""" Places a ca or end cert and key in the script's global store
"""
global ca_certs
global end_certs
ext = cert.get_extension(0)
if ext.get_short_name() == 'basicConstraints':
# compare the bytes for CA:True
if name == "badca" or '0\x03\x01\x01\xff' == ext.get_data():
ca_certs[name] = cert, key
else:
end_certs[name] = cert, key
def writeout_cert_and_key(certdir, name, cert, privkey):
""" Write the cert and key files
"""
writeout_cert(certdir + name + ".crt", cert)
writeout_privkey("keys/" + name + ".key", privkey)
def create_basic_pluto_cas(ca_names):
""" Create the core root certs
"""
print "creating CA certs"
for name in ca_names:
print " - creating %s" % name
ca, key = create_root_ca(CN="Libreswan test CA for " + name,
START=dates['OK_NOW'],
END=dates['FUTURE_END'])
writeout_cert_and_key("cacerts/", name, ca, key)
store_cert_and_key(name, ca, key)
def create_pkcs12(path, name, cert, key, ca_cert):
""" Package and write out a .p12 file
"""
p12 = crypto.PKCS12()
p12.set_certificate(cert)
p12.set_privatekey(key)
p12.set_friendlyname(name)
p12.set_ca_certificates([ca_cert])
with open(path + name + ".p12", "wb") as f:
f.write(p12.export(passphrase="foobar"))
def create_mainca_end_certs(mainca_end_certs):
""" Create the core set of end certs from mainca
"""
serial = 2
print "creating mainca's end certs"
for name in mainca_end_certs:
# put special cert handling here
print " - creating %s" % name
if name == 'bigkey':
keysize = 2048
else:
if name == 'key4096':
keysize = 4096
else:
keysize = 1024
if name == 'notyetvalid':
startdate = dates['FUTURE']
enddate = dates['FUTURE_END']
elif name == 'notvalidanymore':
startdate = dates['OLD']
enddate = dates['OLD_END']
else:
startdate = dates['OK_NOW']
enddate = dates['FUTURE_END']
if name == 'signedbyother':
signer = 'otherca'
elif name[:3] == 'bad':
signer = 'badca'
else:
signer = 'mainca'
if name == 'nic':
ocsp_resp = True
else:
ocsp_resp = False
if name == 'wrongdnorg':
org = "No Such Agency"
else:
org = "Libreswan"
if name == 'unwisechar':
common_name = 'unwisechar ~!@#$%^&*()-'\
'_=+;:/?<>.testing.libreswan.org'
elif name == 'spaceincn':
common_name = 'space invaders.testing.libreswan.org'
elif name == 'cnofca':
common_name = 'Libreswan test CA for mainca'
else:
common_name = name + '.testing.libreswan.org'
if name == 'hashsha2':
alg = 'sha256'
else:
alg = 'sha1'
cert, key = create_sub_cert(common_name,
ca_certs[signer][0],
ca_certs[signer][1],
serial,
O=org,
START=startdate, END=enddate,
keybits=keysize,
sign_alg=alg, ocsp=ocsp_resp)
writeout_cert_and_key("certs/", name, cert, key)
store_cert_and_key(name, cert, key)
create_pkcs12("pkcs12/"+ signer + '/',
name, cert, key, ca_certs[signer][0])
serial += 1
def create_chained_certs(chain_ca_roots, max_path, prefix=''):
""" Create the EE->IA1->IA2->IAx-->CA chains.
Last in the chain is the end cert
TODO: Add more complex trust chain situations
"""
global endrev_name
global top_caname
min_path = 1
ca_cnt = 0
for chainca in chain_ca_roots:
serial = len(end_certs) + ca_cnt
lastca = ""
#note there's an issue with the authkeyid in the chain
#signpair = ()
print "creating %s chain" % chainca
for level in range(min_path, max_path):
cname = prefix + chainca + '_int_' + str(level)
print "level %d cname %s serial %d" % (level, cname,
serial)
if level == min_path:
lastca = "mainca"
signpair = ca_certs[lastca]
print " - creating %s with the last ca of %s" % (cname,
lastca)
ca, key = create_sub_cert(cname +
'.testing.libreswan.org',
signpair[0], signpair[1], serial,
START=dates['OK_NOW'],
END=dates['FUTURE'],
isCA=True, ocsp=False)
writeout_cert_and_key("certs/", cname, ca, key)
store_cert_and_key(cname, ca, key)
lastca = cname
serial += 1
ca_cnt += 1
if level == max_path - 1:
endcert_name = prefix + chainca + "_endcert"
signpair = ca_certs[lastca]
print " - creating %s" % endcert_name
ecert, ekey = create_sub_cert(endcert_name +
".testing.libreswan.org",
signpair[0], signpair[1], serial,
START=dates['OK_NOW'],
END=dates['FUTURE'])
writeout_cert_and_key("certs/", endcert_name,
ecert, ekey)
store_cert_and_key(endcert_name, ecert, ekey)
create_pkcs12("pkcs12/", endcert_name, ecert,
ekey, signpair[0])
serial += 1
endrev_name = prefix + chainca + "_revoked"
top_caname = cname
print " - creating %s" % endrev_name
ercert, erkey = create_sub_cert(endrev_name +
".testing.libreswan.org",
signpair[0], signpair[1], serial,
START=dates['OK_NOW'],
END=dates['FUTURE'])
writeout_cert_and_key("certs/", endrev_name,
ercert, erkey)
store_cert_and_key(endrev_name, ercert, erkey)
create_pkcs12("pkcs12/", endrev_name, ercert,
erkey, signpair[0])
# this special crl was for a openswan/nss freebl combo bug, both of which should
# long be done with.
def create_leading_zero_crl():
""" Create our special crl with a signature that starts out with '00:'
This signs a CRL and checks for a '00' beginning. Each try increments
the days parameter to result in a different signature
"""
zerosig = crypto.CRL()
signcert, signkey = ca_certs['mainca']
days = 1
print "creating a CRL with a leading zero byte signature.."
while True:
good = False
nl = ''
crl = zerosig.export(signcert, signkey,
type=crypto.FILETYPE_TEXT, days=days, digest='sha1')
der = zerosig.export(signcert, signkey,
type=crypto.FILETYPE_ASN1, days=days, digest='sha1')
for index, line in enumerate(crl.splitlines()):
if "Signature Algorithm" in line and index >= 5:
nl = crl.splitlines()[index + 1].strip()
if nl.startswith('00'):
good = True
break
if good:
print nl
print "found after %d signatures!" % (days)
with open("crls/crl-leading-zero-byte.crl", "wb") as f:
f.write(der)
break
days += 1
def create_crlsets():
""" Create test CRLs
"""
print "creating crl set"
revoked = crypto.Revoked()
chainrev = crypto.Revoked()
future_revoked = crypto.Revoked()
revoked.set_rev_date(dates['OK_NOW'])
chainrev.set_rev_date(dates['OK_NOW'])
future_revoked.set_rev_date(dates['FUTURE'])
# the get_serial_number method results in a hex str like '0x17'
# but set_serial needs a hex str like '17'
revoked.set_serial(
hex(end_certs['revoked'][0].get_serial_number())[2:])
chainrev.set_serial(
hex(end_certs['west_chain_revoked'][0].get_serial_number())[2:])
future_revoked.set_serial(
hex(end_certs['revoked'][0].get_serial_number())[2:])
needupdate = crypto.CRL()
needupdate.add_revoked(revoked)
needupdate.add_revoked(chainrev)
with open("crls/needupdate.crl", "wb") as f:
f.write(needupdate.export(ca_certs['mainca'][0],
ca_certs['mainca'][1],
type=crypto.FILETYPE_ASN1,
days=0,
digest='sha1'))
print "sleeping for needupdate/valid crl time difference"
time.sleep(5)
validcrl = crypto.CRL()
validcrl.add_revoked(revoked)
validcrl.add_revoked(chainrev)
with open("crls/cacrlvalid.crl", "wb") as f:
f.write(validcrl.export(ca_certs['mainca'][0],
ca_certs['mainca'][1],
type=crypto.FILETYPE_ASN1,
days=15,
digest='sha1'))
othercrl = crypto.CRL()
othercrl.add_revoked(revoked)
othercrl.add_revoked(chainrev)
with open("crls/othercacrl.crl", "wb") as f:
f.write(othercrl.export(ca_certs['otherca'][0],
ca_certs['otherca'][1],
type=crypto.FILETYPE_ASN1,
days=15,
digest='sha1'))
notyet = crypto.CRL()
notyet.add_revoked(future_revoked)
with open("crls/futurerevoke.crl", "wb") as f:
f.write(notyet.export(ca_certs['mainca'][0],
ca_certs['mainca'][1],
type=crypto.FILETYPE_ASN1,
days=15,
digest='sha1'))
#create_leading_zero_crl()
def create_ec_certs():
""" The OpenSSL module doesn't appear to have
support for curves so we do it with pexpect
"""
print "creating EC certs"
#create CA
pexpect.run('openssl ecparam -out keys/curveca.key '
'-name secp384r1 -genkey -noout')
child = pexpect.spawn('openssl req -x509 '
'-new -key keys/curveca.key '
'-out cacerts/curveca.crt '
'-days 3650 -set_serial 1')
child.expect('Country Name')
child.sendline('CA')
child.expect('State')
child.sendline('Ontario')
child.expect('Locality')
child.sendline('Toronto')
child.expect('Organization')
child.sendline('Libreswan')
child.expect('Organizational')
child.sendline('Test Department')
child.expect('Common')
child.sendline('Libreswan test EC CA')
child.expect('Email')
child.sendline('test...@libreswan.org')
child.expect(pexpect.EOF)
serial = 2
for name in ['east', 'west', 'north', 'road']:
print "- creating %s-ec" % name
#create end certs
pexpect.run('openssl ecparam -out keys/' + name +
'-ec.key -name secp384r1 -genkey
-noout')
child = pexpect.spawn('openssl req -x509 '
'-new -key
keys/curveca.key '
'-out certs/' + name +
'-ec.crt -days 365
-set_serial ' +
str(serial))
child.expect('Country Name')
child.sendline('CA')
child.expect('State')
child.sendline('Ontario')
child.expect('Locality')
child.sendline('Toronto')
child.expect('Organization')
child.sendline('Libreswan')
child.expect('Organizational')
child.sendline('Test Department')
child.expect('Common')
child.sendline(name + '-ec.testing.libreswan.org')
child.expect('Email')
child.sendline('test...@libreswan.org')
child.expect(pexpect.EOF)
serial += 1
#package p12
pexpect.run('openssl pkcs12 -export '
'-inkey keys/%s-ec.key '
'-in certs/%s-ec.crt -name %s-ec '
'-certfile cacerts/curveca.crt '
'-caname "curveca" '
'-out pkcs12/curveca/%s-ec.p12 '
'-passin pass:foobar -passout
pass:foobar'
% (name, name, name, name))
def run_dist_certs():
""" Generate the pluto test harness x509
certificates, p12 files, keys, and CRLs
"""
# Add root CAs here
basic_pluto_cas = ('mainca', 'otherca', 'badca')
# Add end certs here
mainca_end_certs = ('nic','east','west', 'road', 'sunset',
'sunrise','north','south',
'pole','park','beet','carrot',
'usage-server', 'usage-client',
'usage-both',
'nic-noext', 'nic-nourl',
'japan','bigkey', 'key4096',
'notyetvalid','notvalidanymore',
'signedbyother','wrongdnorg',
'unwisechar','spaceincn','hashsha2',
'cnofca','revoked', 'badwest',
'badeast')
# Add chain roots here
chain_ca_roots = ('east_chain', 'west_chain')
# Put special case code for new certs in the following functions
create_basic_pluto_cas(basic_pluto_cas)
create_mainca_end_certs(mainca_end_certs)
create_chained_certs(chain_ca_roots, 3)
create_chained_certs(chain_ca_roots, 9, 'long_')
create_chained_certs(chain_ca_roots, 10, 'too_long_')
create_crlsets()
create_ec_certs()
def create_nss_pw():
print "creating nss-pw"
f = open("nss-pw","w")
f.write("foobar")
f.close()
def main():
outdir = os.path.dirname(sys.argv[0])
cwd = os.getcwd()
os.chdir(outdir)
global dates
reset_files()
dates = gen_gmtime_dates()
print "format dates being used for this run:"
# TODO: print the display GMT times
for n, s in dates.iteritems():
print "%s : %s" % (n, s)
run_dist_certs()
create_nss_pw()
os.chdir(cwd)
print "finished!"
if __name__ == "__main__":
main()
--
dev-tech-crypto mailing list
dev-tech-crypto@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-crypto