cpython/Lib/test/make_ssl_certs.py

283 lines
8.5 KiB
Python

"""Make the custom certificate and private key files used by test_ssl
and friends."""
import os
import pprint
import shutil
import tempfile
from subprocess import *
req_template = """
[ default ]
base_url = http://testca.pythontest.net/testca
[req]
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = XY
L = Castle Anthrax
O = Python Software Foundation
CN = {hostname}
[req_x509_extensions_simple]
subjectAltName = @san
[req_x509_extensions_full]
subjectAltName = @san
keyUsage = critical,keyEncipherment,digitalSignature
extendedKeyUsage = serverAuth,clientAuth
basicConstraints = critical,CA:false
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
authorityInfoAccess = @issuer_ocsp_info
crlDistributionPoints = @crl_info
[ issuer_ocsp_info ]
caIssuers;URI.0 = $base_url/pycacert.cer
OCSP;URI.0 = $base_url/ocsp/
[ crl_info ]
URI.0 = $base_url/revocation.crl
[san]
DNS.1 = {hostname}
{extra_san}
[dir_sect]
C = XY
L = Castle Anthrax
O = Python Software Foundation
CN = dirname example
[princ_name]
realm = EXP:0, GeneralString:KERBEROS.REALM
principal_name = EXP:1, SEQUENCE:principal_seq
[principal_seq]
name_type = EXP:0, INTEGER:1
name_string = EXP:1, SEQUENCE:principals
[principals]
princ1 = GeneralString:username
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = cadir
database = $dir/index.txt
crlnumber = $dir/crl.txt
default_md = sha256
default_days = 3600
default_crl_days = 3600
certificate = pycacert.pem
private_key = pycakey.pem
serial = $dir/serial
RANDFILE = $dir/.rand
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true
"""
here = os.path.abspath(os.path.dirname(__file__))
def make_cert_key(hostname, sign=False, extra_san='',
ext='req_x509_extensions_full', key='rsa:3072'):
print("creating cert for " + hostname)
tempnames = []
for i in range(3):
with tempfile.NamedTemporaryFile(delete=False) as f:
tempnames.append(f.name)
req_file, cert_file, key_file = tempnames
try:
req = req_template.format(hostname=hostname, extra_san=extra_san)
with open(req_file, 'w') as f:
f.write(req)
args = ['req', '-new', '-days', '3650', '-nodes',
'-newkey', key, '-keyout', key_file,
'-extensions', ext,
'-config', req_file]
if sign:
with tempfile.NamedTemporaryFile(delete=False) as f:
tempnames.append(f.name)
reqfile = f.name
args += ['-out', reqfile ]
else:
args += ['-x509', '-out', cert_file ]
check_call(['openssl'] + args)
if sign:
args = [
'ca',
'-config', req_file,
'-extensions', ext,
'-out', cert_file,
'-outdir', 'cadir',
'-policy', 'policy_anything',
'-batch', '-infiles', reqfile
]
check_call(['openssl'] + args)
with open(cert_file, 'r') as f:
cert = f.read()
with open(key_file, 'r') as f:
key = f.read()
return cert, key
finally:
for name in tempnames:
os.remove(name)
TMP_CADIR = 'cadir'
def unmake_ca():
shutil.rmtree(TMP_CADIR)
def make_ca():
os.mkdir(TMP_CADIR)
with open(os.path.join('cadir','index.txt'),'a+') as f:
pass # empty file
with open(os.path.join('cadir','crl.txt'),'a+') as f:
f.write("00")
with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
f.write('unique_subject = no')
with tempfile.NamedTemporaryFile("w") as t:
t.write(req_template.format(hostname='our-ca-server', extra_san=''))
t.flush()
with tempfile.NamedTemporaryFile() as f:
args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes',
'-newkey', 'rsa:3072', '-keyout', 'pycakey.pem',
'-out', f.name,
'-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=our-ca-server']
check_call(['openssl'] + args)
args = ['ca', '-config', t.name, '-create_serial',
'-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR,
'-keyfile', 'pycakey.pem', '-days', '3650',
'-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ]
check_call(['openssl'] + args)
args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl']
check_call(['openssl'] + args)
# capath hashes depend on subject!
check_call([
'openssl', 'x509', '-in', 'pycacert.pem', '-out', 'capath/ceff1710.0'
])
shutil.copy('capath/ceff1710.0', 'capath/b1930218.0')
def print_cert(path):
import _ssl
pprint.pprint(_ssl._test_decode_cert(path))
if __name__ == '__main__':
os.chdir(here)
cert, key = make_cert_key('localhost', ext='req_x509_extensions_simple')
with open('ssl_cert.pem', 'w') as f:
f.write(cert)
with open('ssl_key.pem', 'w') as f:
f.write(key)
print("password protecting ssl_key.pem in ssl_key.passwd.pem")
check_call(['openssl','pkey','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-aes256','-passout','pass:somepass'])
check_call(['openssl','pkey','-in','ssl_key.pem','-out','keycert.passwd.pem','-aes256','-passout','pass:somepass'])
with open('keycert.pem', 'w') as f:
f.write(key)
f.write(cert)
with open('keycert.passwd.pem', 'a+') as f:
f.write(cert)
# For certificate matching tests
make_ca()
cert, key = make_cert_key('fakehostname', ext='req_x509_extensions_simple')
with open('keycert2.pem', 'w') as f:
f.write(key)
f.write(cert)
cert, key = make_cert_key('localhost', True)
with open('keycert3.pem', 'w') as f:
f.write(key)
f.write(cert)
cert, key = make_cert_key('fakehostname', True)
with open('keycert4.pem', 'w') as f:
f.write(key)
f.write(cert)
cert, key = make_cert_key(
'localhost-ecc', True, key='param:secp384r1.pem'
)
with open('keycertecc.pem', 'w') as f:
f.write(key)
f.write(cert)
extra_san = [
'otherName.1 = 1.2.3.4;UTF8:some other identifier',
'otherName.2 = 1.3.6.1.5.2.2;SEQUENCE:princ_name',
'email.1 = user@example.org',
'DNS.2 = www.example.org',
# GEN_X400
'dirName.1 = dir_sect',
# GEN_EDIPARTY
'URI.1 = https://www.python.org/',
'IP.1 = 127.0.0.1',
'IP.2 = ::1',
'RID.1 = 1.2.3.4.5',
]
cert, key = make_cert_key('allsans', extra_san='\n'.join(extra_san))
with open('allsans.pem', 'w') as f:
f.write(key)
f.write(cert)
extra_san = [
# könig (king)
'DNS.2 = xn--knig-5qa.idn.pythontest.net',
# königsgäßchen (king's alleyway)
'DNS.3 = xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
'DNS.4 = xn--knigsgchen-b4a3dun.idna2008.pythontest.net',
# βόλοσ (marble)
'DNS.5 = xn--nxasmq6b.idna2003.pythontest.net',
'DNS.6 = xn--nxasmm1c.idna2008.pythontest.net',
]
# IDN SANS, signed
cert, key = make_cert_key('idnsans', True, extra_san='\n'.join(extra_san))
with open('idnsans.pem', 'w') as f:
f.write(key)
f.write(cert)
unmake_ca()
print("update Lib/test/test_ssl.py and Lib/test/test_asyncio/util.py")
print_cert('keycert.pem')
print_cert('keycert3.pem')