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