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