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