mirror of
https://github.com/python/cpython.git
synced 2026-02-26 09:01:08 +00:00
gh-120762: make_ssl_certs: Don't set extensions for the CSR
`openssl req` fails with openssl 3.2.2 because the config line
authorityKeyIdentifier = keyid:always,issuer:always
is not supported for certificate signing requests (since the issuing
certificate authority is not known).
David von Oheimb, the OpenSSL dev that made the change, commented in:
https://github.com/openssl/openssl/issues/22966#issuecomment-1858396738 :
> This problem did not show up in older OpenSSL versions because of a bug:
> the `req` app ignored the `-extensions` option unless `-x505` is given,
> which I fixed in https://github.com/openssl/openssl/pull/16865.
(I assume `-x505` is a typo for `-x509`.)
In our `make_cert_key` function:
If `sign` is true:
- We don't pass `-x509` to `req`, so in this case it should be safe to
omit the `-extensions` argument. (Old OpenSSL ignores it, new OpenSSL
fails on it.)
- The extensions are passed to the `ca` call later in the function.
There they take effect, and `authorityKeyIdentifier` is valid.
If `sign` is false, this commit has no effect except rearranging the
CLI arguments.
326 lines
10 KiB
Python
326 lines
10 KiB
Python
"""Make the custom certificate and private key files used by test_ssl
|
|
and friends."""
|
|
|
|
import argparse
|
|
import os
|
|
import pprint
|
|
import shutil
|
|
import tempfile
|
|
from subprocess import *
|
|
|
|
startdate = "20180829142316Z"
|
|
enddate_default = "20371028142316Z"
|
|
days_default = "7000"
|
|
|
|
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_nosan]
|
|
|
|
[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
|
|
startdate = {startdate}
|
|
default_startdate = {startdate}
|
|
enddate = {enddate}
|
|
default_enddate = {enddate}
|
|
default_days = {days}
|
|
default_crl_days = {days}
|
|
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 = critical, CA:true
|
|
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
|
|
|
|
"""
|
|
|
|
here = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
|
|
def make_cert_key(cmdlineargs, 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,
|
|
startdate=startdate,
|
|
enddate=cmdlineargs.enddate,
|
|
days=cmdlineargs.days
|
|
)
|
|
with open(req_file, 'w') as f:
|
|
f.write(req)
|
|
args = ['req', '-new', '-nodes', '-days', cmdlineargs.days,
|
|
'-newkey', key, '-keyout', key_file,
|
|
'-config', req_file]
|
|
if sign:
|
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
|
tempnames.append(f.name)
|
|
reqfile = f.name
|
|
args += ['-out', reqfile ]
|
|
|
|
else:
|
|
args += ['-extensions', ext, '-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(cmdlineargs):
|
|
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')
|
|
# random start value for serial numbers
|
|
with open(os.path.join('cadir','serial'), 'w') as f:
|
|
f.write('CB2D80995A69525B\n')
|
|
|
|
with tempfile.NamedTemporaryFile("w") as t:
|
|
req = req_template.format(
|
|
hostname='our-ca-server',
|
|
extra_san='',
|
|
startdate=startdate,
|
|
enddate=cmdlineargs.enddate,
|
|
days=cmdlineargs.days
|
|
)
|
|
t.write(req)
|
|
t.flush()
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
args = ['req', '-config', t.name, '-new',
|
|
'-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,
|
|
'-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR,
|
|
'-keyfile', 'pycakey.pem',
|
|
'-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 write_cert_reference(path):
|
|
import _ssl
|
|
refdata = pprint.pformat(_ssl._test_decode_cert(path))
|
|
print(refdata)
|
|
with open(path + '.reference', 'w') as f:
|
|
print(refdata, file=f)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description='Make the custom certificate and private key files used by test_ssl and friends.')
|
|
parser.add_argument('--days', default=days_default)
|
|
parser.add_argument('--enddate', default=enddate_default)
|
|
cmdlineargs = parser.parse_args()
|
|
|
|
os.chdir(here)
|
|
cert, key = make_cert_key(cmdlineargs, '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(cmdlineargs)
|
|
cert, key = make_cert_key(cmdlineargs, 'fakehostname', ext='req_x509_extensions_simple')
|
|
with open('keycert2.pem', 'w') as f:
|
|
f.write(key)
|
|
f.write(cert)
|
|
|
|
cert, key = make_cert_key(cmdlineargs, 'localhost', sign=True)
|
|
with open('keycert3.pem', 'w') as f:
|
|
f.write(key)
|
|
f.write(cert)
|
|
|
|
check_call(['openssl', 'x509', '-outform', 'pem', '-in', 'keycert3.pem', '-out', 'cert3.pem'])
|
|
|
|
cert, key = make_cert_key(cmdlineargs, 'fakehostname', sign=True)
|
|
with open('keycert4.pem', 'w') as f:
|
|
f.write(key)
|
|
f.write(cert)
|
|
|
|
cert, key = make_cert_key(
|
|
cmdlineargs, 'localhost-ecc', sign=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(cmdlineargs, 'allsans', sign=True, 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(cmdlineargs, 'idnsans', sign=True, extra_san='\n'.join(extra_san))
|
|
with open('idnsans.pem', 'w') as f:
|
|
f.write(key)
|
|
f.write(cert)
|
|
|
|
cert, key = make_cert_key(cmdlineargs, 'nosan', sign=True, ext='req_x509_extensions_nosan')
|
|
with open('nosan.pem', 'w') as f:
|
|
f.write(key)
|
|
f.write(cert)
|
|
|
|
unmake_ca()
|
|
print("Writing out reference data for Lib/test/test_ssl.py and Lib/test/test_asyncio/utils.py")
|
|
write_cert_reference('keycert.pem')
|
|
write_cert_reference('keycert3.pem')
|