• 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 = CA:true
113
114    """
115
116here = os.path.abspath(os.path.dirname(__file__))
117
118
119def make_cert_key(hostname, sign=False, extra_san='',
120                  ext='req_x509_extensions_full', key='rsa:3072'):
121    print("creating cert for " + hostname)
122    tempnames = []
123    for i in range(3):
124        with tempfile.NamedTemporaryFile(delete=False) as f:
125            tempnames.append(f.name)
126    req_file, cert_file, key_file = tempnames
127    try:
128        req = req_template.format(
129            hostname=hostname,
130            extra_san=extra_san,
131            startdate=startdate,
132            enddate=enddate
133        )
134        with open(req_file, 'w') as f:
135            f.write(req)
136        args = ['req', '-new', '-nodes', '-days', '7000',
137                '-newkey', key, '-keyout', key_file,
138                '-extensions', ext,
139                '-config', req_file]
140        if sign:
141            with tempfile.NamedTemporaryFile(delete=False) as f:
142                tempnames.append(f.name)
143                reqfile = f.name
144            args += ['-out', reqfile ]
145
146        else:
147            args += ['-x509', '-out', cert_file ]
148        check_call(['openssl'] + args)
149
150        if sign:
151            args = [
152                'ca',
153                '-config', req_file,
154                '-extensions', ext,
155                '-out', cert_file,
156                '-outdir', 'cadir',
157                '-policy', 'policy_anything',
158                '-batch', '-infiles', reqfile
159            ]
160            check_call(['openssl'] + args)
161
162
163        with open(cert_file, 'r') as f:
164            cert = f.read()
165        with open(key_file, 'r') as f:
166            key = f.read()
167        return cert, key
168    finally:
169        for name in tempnames:
170            os.remove(name)
171
172TMP_CADIR = 'cadir'
173
174def unmake_ca():
175    shutil.rmtree(TMP_CADIR)
176
177def make_ca():
178    os.mkdir(TMP_CADIR)
179    with open(os.path.join('cadir','index.txt'),'a+') as f:
180        pass # empty file
181    with open(os.path.join('cadir','crl.txt'),'a+') as f:
182        f.write("00")
183    with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
184        f.write('unique_subject = no')
185    # random start value for serial numbers
186    with open(os.path.join('cadir','serial'), 'w') as f:
187        f.write('CB2D80995A69525B\n')
188
189    with tempfile.NamedTemporaryFile("w") as t:
190        req = req_template.format(
191            hostname='our-ca-server',
192            extra_san='',
193            startdate=startdate,
194            enddate=enddate
195        )
196        t.write(req)
197        t.flush()
198        with tempfile.NamedTemporaryFile() as f:
199            args = ['req', '-config', t.name, '-new',
200                    '-nodes',
201                    '-newkey', 'rsa:3072',
202                    '-keyout', 'pycakey.pem',
203                    '-out', f.name,
204                    '-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=our-ca-server']
205            check_call(['openssl'] + args)
206            args = ['ca', '-config', t.name,
207                    '-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR,
208                    '-keyfile', 'pycakey.pem',
209                    '-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ]
210            check_call(['openssl'] + args)
211            args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl']
212            check_call(['openssl'] + args)
213
214    # capath hashes depend on subject!
215    check_call([
216        'openssl', 'x509', '-in', 'pycacert.pem', '-out', 'capath/ceff1710.0'
217    ])
218    shutil.copy('capath/ceff1710.0', 'capath/b1930218.0')
219
220
221def print_cert(path):
222    import _ssl
223    pprint.pprint(_ssl._test_decode_cert(path))
224
225
226if __name__ == '__main__':
227    os.chdir(here)
228    cert, key = make_cert_key('localhost', ext='req_x509_extensions_simple')
229    with open('ssl_cert.pem', 'w') as f:
230        f.write(cert)
231    with open('ssl_key.pem', 'w') as f:
232        f.write(key)
233    print("password protecting ssl_key.pem in ssl_key.passwd.pem")
234    check_call(['openssl','pkey','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-aes256','-passout','pass:somepass'])
235    check_call(['openssl','pkey','-in','ssl_key.pem','-out','keycert.passwd.pem','-aes256','-passout','pass:somepass'])
236
237    with open('keycert.pem', 'w') as f:
238        f.write(key)
239        f.write(cert)
240
241    with open('keycert.passwd.pem', 'a+') as f:
242        f.write(cert)
243
244    # For certificate matching tests
245    make_ca()
246    cert, key = make_cert_key('fakehostname', ext='req_x509_extensions_simple')
247    with open('keycert2.pem', 'w') as f:
248        f.write(key)
249        f.write(cert)
250
251    cert, key = make_cert_key('localhost', sign=True)
252    with open('keycert3.pem', 'w') as f:
253        f.write(key)
254        f.write(cert)
255
256    cert, key = make_cert_key('fakehostname', sign=True)
257    with open('keycert4.pem', 'w') as f:
258        f.write(key)
259        f.write(cert)
260
261    cert, key = make_cert_key(
262        'localhost-ecc', sign=True, key='param:secp384r1.pem'
263    )
264    with open('keycertecc.pem', 'w') as f:
265        f.write(key)
266        f.write(cert)
267
268    extra_san = [
269        'otherName.1 = 1.2.3.4;UTF8:some other identifier',
270        'otherName.2 = 1.3.6.1.5.2.2;SEQUENCE:princ_name',
271        'email.1 = user@example.org',
272        'DNS.2 = www.example.org',
273        # GEN_X400
274        'dirName.1 = dir_sect',
275        # GEN_EDIPARTY
276        'URI.1 = https://www.python.org/',
277        'IP.1 = 127.0.0.1',
278        'IP.2 = ::1',
279        'RID.1 = 1.2.3.4.5',
280    ]
281
282    cert, key = make_cert_key('allsans', sign=True, extra_san='\n'.join(extra_san))
283    with open('allsans.pem', 'w') as f:
284        f.write(key)
285        f.write(cert)
286
287    extra_san = [
288        # könig (king)
289        'DNS.2 = xn--knig-5qa.idn.pythontest.net',
290        # königsgäßchen (king's alleyway)
291        'DNS.3 = xn--knigsgsschen-lcb0w.idna2003.pythontest.net',
292        'DNS.4 = xn--knigsgchen-b4a3dun.idna2008.pythontest.net',
293        # βόλοσ (marble)
294        'DNS.5 = xn--nxasmq6b.idna2003.pythontest.net',
295        'DNS.6 = xn--nxasmm1c.idna2008.pythontest.net',
296    ]
297
298    # IDN SANS, signed
299    cert, key = make_cert_key('idnsans', sign=True, extra_san='\n'.join(extra_san))
300    with open('idnsans.pem', 'w') as f:
301        f.write(key)
302        f.write(cert)
303
304    cert, key = make_cert_key('nosan', sign=True, ext='req_x509_extensions_nosan')
305    with open('nosan.pem', 'w') as f:
306        f.write(key)
307        f.write(cert)
308
309    unmake_ca()
310    print("update Lib/test/test_ssl.py and Lib/test/test_asyncio/utils.py")
311    print_cert('keycert.pem')
312    print_cert('keycert3.pem')
313