1 """Make the custom certificate and private key files used by test_ssl
2 and friends."""
3
4 import os
5 import pprint
6 import shutil
7 import tempfile
8 from subprocess import *
9
10 startdate = "20180829142316Z"
11 enddate = "20371028142316Z"
12
13 req_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
116 here = os.path.abspath(os.path.dirname(__file__))
117
118
119 def 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
172 TMP_CADIR = 'cadir'
173
174 def unmake_ca():
175 shutil.rmtree(TMP_CADIR)
176
177 def 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
221 def print_cert(path):
222 import _ssl
223 pprint.pprint(_ssl._test_decode_cert(path))
224
225
226 if __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')