make_ssl_certs.py 9.2 KB

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