• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Make the custom certificate and private key files used by test_ssl
2and friends."""
3
4import os
5import pprint
6import shutil
7import tempfile
8from subprocess import *
9
10req_template = """
11    [ default ]
12    base_url               = http://testca.pythontest.net/testca
13
14    [req]
15    distinguished_name     = req_distinguished_name
16    prompt                 = no
17
18    [req_distinguished_name]
19    C                      = XY
20    L                      = Castle Anthrax
21    O                      = Python Software Foundation
22    CN                     = {hostname}
23
24    [req_x509_extensions_simple]
25    subjectAltName         = @san
26
27    [req_x509_extensions_full]
28    subjectAltName         = @san
29    keyUsage               = critical,keyEncipherment,digitalSignature
30    extendedKeyUsage       = serverAuth,clientAuth
31    basicConstraints       = critical,CA:false
32    subjectKeyIdentifier   = hash
33    authorityKeyIdentifier = keyid:always,issuer:always
34    authorityInfoAccess    = @issuer_ocsp_info
35    crlDistributionPoints  = @crl_info
36
37    [ issuer_ocsp_info ]
38    caIssuers;URI.0        = $base_url/pycacert.cer
39    OCSP;URI.0             = $base_url/ocsp/
40
41    [ crl_info ]
42    URI.0                  = $base_url/revocation.crl
43
44    [san]
45    DNS.1 = {hostname}
46    {extra_san}
47
48    [dir_sect]
49    C                      = XY
50    L                      = Castle Anthrax
51    O                      = Python Software Foundation
52    CN                     = dirname example
53
54    [princ_name]
55    realm = EXP:0, GeneralString:KERBEROS.REALM
56    principal_name = EXP:1, SEQUENCE:principal_seq
57
58    [principal_seq]
59    name_type = EXP:0, INTEGER:1
60    name_string = EXP:1, SEQUENCE:principals
61
62    [principals]
63    princ1 = GeneralString:username
64
65    [ ca ]
66    default_ca      = CA_default
67
68    [ CA_default ]
69    dir = cadir
70    database  = $dir/index.txt
71    crlnumber = $dir/crl.txt
72    default_md = sha256
73    default_days = 3600
74    default_crl_days = 3600
75    certificate = pycacert.pem
76    private_key = pycakey.pem
77    serial    = $dir/serial
78    RANDFILE  = $dir/.rand
79    policy          = policy_match
80
81    [ policy_match ]
82    countryName             = match
83    stateOrProvinceName     = optional
84    organizationName        = match
85    organizationalUnitName  = optional
86    commonName              = supplied
87    emailAddress            = optional
88
89    [ policy_anything ]
90    countryName   = optional
91    stateOrProvinceName = optional
92    localityName    = optional
93    organizationName  = optional
94    organizationalUnitName  = optional
95    commonName    = supplied
96    emailAddress    = optional
97
98
99    [ v3_ca ]
100
101    subjectKeyIdentifier=hash
102    authorityKeyIdentifier=keyid:always,issuer
103    basicConstraints = CA:true
104
105    """
106
107here = os.path.abspath(os.path.dirname(__file__))
108
109
110def make_cert_key(hostname, sign=False, extra_san='',
111                  ext='req_x509_extensions_full', key='rsa:3072'):
112    print("creating cert for " + hostname)
113    tempnames = []
114    for i in range(3):
115        with tempfile.NamedTemporaryFile(delete=False) as f:
116            tempnames.append(f.name)
117    req_file, cert_file, key_file = tempnames
118    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                '-newkey', key, '-keyout', key_file,
124                '-extensions', ext,
125                '-config', req_file]
126        if sign:
127            with tempfile.NamedTemporaryFile(delete=False) as f:
128                tempnames.append(f.name)
129                reqfile = f.name
130            args += ['-out', reqfile ]
131
132        else:
133            args += ['-x509', '-out', cert_file ]
134        check_call(['openssl'] + args)
135
136        if sign:
137            args = [
138                'ca',
139                '-config', req_file,
140                '-extensions', ext,
141                '-out', cert_file,
142                '-outdir', 'cadir',
143                '-policy', 'policy_anything',
144                '-batch', '-infiles', reqfile
145            ]
146            check_call(['openssl'] + args)
147
148
149        with open(cert_file, 'r') as f:
150            cert = f.read()
151        with open(key_file, 'r') as f:
152            key = f.read()
153        return cert, key
154    finally:
155        for name in tempnames:
156            os.remove(name)
157
158TMP_CADIR = 'cadir'
159
160def unmake_ca():
161    shutil.rmtree(TMP_CADIR)
162
163def make_ca():
164    os.mkdir(TMP_CADIR)
165    with open(os.path.join('cadir','index.txt'),'a+') as f:
166        pass # empty file
167    with open(os.path.join('cadir','crl.txt'),'a+') as f:
168        f.write("00")
169    with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
170        f.write('unique_subject = no')
171
172    with tempfile.NamedTemporaryFile("w") as t:
173        t.write(req_template.format(hostname='our-ca-server', extra_san=''))
174        t.flush()
175        with tempfile.NamedTemporaryFile() as f:
176            args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes',
177                    '-newkey', 'rsa:3072', '-keyout', 'pycakey.pem',
178                    '-out', f.name,
179                    '-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=our-ca-server']
180            check_call(['openssl'] + args)
181            args = ['ca', '-config', t.name, '-create_serial',
182                    '-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR,
183                    '-keyfile', 'pycakey.pem', '-days', '3650',
184                    '-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ]
185            check_call(['openssl'] + args)
186            args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl']
187            check_call(['openssl'] + args)
188
189    # capath hashes depend on subject!
190    check_call([
191        'openssl', 'x509', '-in', 'pycacert.pem', '-out', 'capath/ceff1710.0'
192    ])
193    shutil.copy('capath/ceff1710.0', 'capath/b1930218.0')
194
195
196def print_cert(path):
197    import _ssl
198    pprint.pprint(_ssl._test_decode_cert(path))
199
200
201if __name__ == '__main__':
202    os.chdir(here)
203    cert, key = make_cert_key('localhost', ext='req_x509_extensions_simple')
204    with open('ssl_cert.pem', 'w') as f:
205        f.write(cert)
206    with open('ssl_key.pem', 'w') as f:
207        f.write(key)
208    print("password protecting ssl_key.pem in ssl_key.passwd.pem")
209    check_call(['openssl','pkey','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-aes256','-passout','pass:somepass'])
210    check_call(['openssl','pkey','-in','ssl_key.pem','-out','keycert.passwd.pem','-aes256','-passout','pass:somepass'])
211
212    with open('keycert.pem', 'w') as f:
213        f.write(key)
214        f.write(cert)
215
216    with open('keycert.passwd.pem', 'a+') as f:
217        f.write(cert)
218
219    # For certificate matching tests
220    make_ca()
221    cert, key = make_cert_key('fakehostname', ext='req_x509_extensions_simple')
222    with open('keycert2.pem', 'w') as f:
223        f.write(key)
224        f.write(cert)
225
226    cert, key = make_cert_key('localhost', True)
227    with open('keycert3.pem', 'w') as f:
228        f.write(key)
229        f.write(cert)
230
231    cert, key = make_cert_key('fakehostname', True)
232    with open('keycert4.pem', 'w') as f:
233        f.write(key)
234        f.write(cert)
235
236    cert, key = make_cert_key(
237        'localhost-ecc', True, key='param:secp384r1.pem'
238    )
239    with open('keycertecc.pem', 'w') as f:
240        f.write(key)
241        f.write(cert)
242
243    extra_san = [
244        'otherName.1 = 1.2.3.4;UTF8:some other identifier',
245        'otherName.2 = 1.3.6.1.5.2.2;SEQUENCE:princ_name',
246        'email.1 = user@example.org',
247        'DNS.2 = www.example.org',
248        # GEN_X400
249        'dirName.1 = dir_sect',
250        # GEN_EDIPARTY
251        'URI.1 = https://www.python.org/',
252        'IP.1 = 127.0.0.1',
253        'IP.2 = ::1',
254        'RID.1 = 1.2.3.4.5',
255    ]
256
257    cert, key = make_cert_key('allsans', extra_san='\n'.join(extra_san))
258    with open('allsans.pem', 'w') as f:
259        f.write(key)
260        f.write(cert)
261
262    extra_san = [
263        # könig (king)
264        'DNS.2 = xn--knig-5qa.idn.pythontest.net',
265        # königsgäßchen (king's alleyway)
266        'DNS.3 = xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
267        'DNS.4 = xn--knigsgchen-b4a3dun.idna2008.pythontest.net',
268        # βόλοσ (marble)
269        'DNS.5 = xn--nxasmq6b.idna2003.pythontest.net',
270        'DNS.6 = xn--nxasmm1c.idna2008.pythontest.net',
271    ]
272
273    # IDN SANS, signed
274    cert, key = make_cert_key('idnsans', True, extra_san='\n'.join(extra_san))
275    with open('idnsans.pem', 'w') as f:
276        f.write(key)
277        f.write(cert)
278
279    unmake_ca()
280    print("update Lib/test/test_ssl.py and Lib/test/test_asyncio/util.py")
281    print_cert('keycert.pem')
282    print_cert('keycert3.pem')
283