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