mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	The private keys for test_ssl were encrypted with 3DES in traditional PKCS#5 format. 3DES and the digest algorithm of PKCS#5 are blocked by some strict crypto policies. Use PKCS#8 format with AES256 encryption instead. Signed-off-by: Christian Heimes <christian@python.org> https://bugs.python.org/issue38271 Automerge-Triggered-By: @tiran
		
			
				
	
	
		
			282 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			282 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')
 |