make_ssl_certs.py 8.52 KB
Newer Older
1 2 3 4
"""Make the custom certificate and private key files used by test_ssl
and friends."""

import os
5
import pprint
6
import shutil
7 8 9 10
import tempfile
from subprocess import *

req_template = """
11 12 13
    [ default ]
    base_url               = http://testca.pythontest.net/testca

14 15 16 17 18 19 20 21 22 23
    [req]
    distinguished_name     = req_distinguished_name
    prompt                 = no

    [req_distinguished_name]
    C                      = XY
    L                      = Castle Anthrax
    O                      = Python Software Foundation
    CN                     = {hostname}

24 25 26 27
    [req_x509_extensions_simple]
    subjectAltName         = @san

    [req_x509_extensions_full]
28
    subjectAltName         = @san
29 30 31 32 33 34 35 36 37 38 39 40 41 42
    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
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

    [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
64 65 66 67 68 69 70

    [ ca ]
    default_ca      = CA_default

    [ CA_default ]
    dir = cadir
    database  = $dir/index.txt
71
    crlnumber = $dir/crl.txt
72 73
    default_md = sha1
    default_days = 3600
74
    default_crl_days = 3600
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    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

105 106 107 108
    """

here = os.path.abspath(os.path.dirname(__file__))

109 110 111

def make_cert_key(hostname, sign=False, extra_san='',
                  ext='req_x509_extensions_full', key='rsa:2048'):
112
    print("creating cert for " + hostname)
113 114 115 116 117 118
    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:
119
        req = req_template.format(hostname=hostname, extra_san=extra_san)
120
        with open(req_file, 'w') as f:
121
            f.write(req)
122
        args = ['req', '-new', '-days', '3650', '-nodes',
123 124
                '-newkey', key, '-keyout', key_file,
                '-extensions', ext,
125 126 127 128 129 130 131 132 133
                '-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 ]
134
        check_call(['openssl'] + args)
135 136

        if sign:
137 138 139 140 141 142 143 144 145
            args = [
                'ca',
                '-config', req_file,
                '-extensions', ext,
                '-out', cert_file,
                '-outdir', 'cadir',
                '-policy', 'policy_anything',
                '-batch', '-infiles', reqfile
            ]
146 147 148
            check_call(['openssl'] + args)


149 150 151 152 153 154 155 156 157
        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)

158 159 160 161 162 163 164 165 166
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
167
    with open(os.path.join('cadir','crl.txt'),'a+') as f:
168
        f.write("00")
169 170 171 172
    with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
        f.write('unique_subject = no')

    with tempfile.NamedTemporaryFile("w") as t:
173
        t.write(req_template.format(hostname='our-ca-server', extra_san=''))
174 175 176 177 178 179 180 181 182 183 184 185
        t.flush()
        with tempfile.NamedTemporaryFile() as f:
            args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes',
                    '-newkey', 'rsa:2048', '-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)
186 187
            args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl']
            check_call(['openssl'] + args)
188

189 190 191 192 193 194 195 196 197 198 199 200
    # 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))


201 202
if __name__ == '__main__':
    os.chdir(here)
203
    cert, key = make_cert_key('localhost', ext='req_x509_extensions_simple')
204 205 206 207
    with open('ssl_cert.pem', 'w') as f:
        f.write(cert)
    with open('ssl_key.pem', 'w') as f:
        f.write(key)
208 209 210 211
    print("password protecting ssl_key.pem in ssl_key.passwd.pem")
    check_call(['openssl','rsa','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-des3','-passout','pass:somepass'])
    check_call(['openssl','rsa','-in','ssl_key.pem','-out','keycert.passwd.pem','-des3','-passout','pass:somepass'])

212 213 214
    with open('keycert.pem', 'w') as f:
        f.write(key)
        f.write(cert)
215 216 217 218

    with open('keycert.passwd.pem', 'a+') as f:
        f.write(cert)

219
    # For certificate matching tests
220
    make_ca()
221
    cert, key = make_cert_key('fakehostname', ext='req_x509_extensions_simple')
222 223 224
    with open('keycert2.pem', 'w') as f:
        f.write(key)
        f.write(cert)
225 226 227 228 229 230 231 232 233 234 235

    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)

236 237 238 239 240 241 242
    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)

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    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)

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    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)

279
    unmake_ca()
280 281 282
    print("update Lib/test/test_ssl.py and Lib/test/test_asyncio/util.py")
    print_cert('keycert.pem')
    print_cert('keycert3.pem')