1"""Make the custom certificate and private key files used by test_ssl
2and friends."""
3
4import os
5import shutil
6import sys
7import tempfile
8from subprocess import *
9
10req_template = """
11    [req]
12    distinguished_name     = req_distinguished_name
13    x509_extensions        = req_x509_extensions
14    prompt                 = no
15
16    [req_distinguished_name]
17    C                      = XY
18    L                      = Castle Anthrax
19    O                      = Python Software Foundation
20    CN                     = {hostname}
21
22    [req_x509_extensions]
23    subjectAltName         = @san
24
25    [san]
26    DNS.1 = {hostname}
27    {extra_san}
28
29    [dir_sect]
30    C                      = XY
31    L                      = Castle Anthrax
32    O                      = Python Software Foundation
33    CN                     = dirname example
34
35    [princ_name]
36    realm = EXP:0, GeneralString:KERBEROS.REALM
37    principal_name = EXP:1, SEQUENCE:principal_seq
38
39    [principal_seq]
40    name_type = EXP:0, INTEGER:1
41    name_string = EXP:1, SEQUENCE:principals
42
43    [principals]
44    princ1 = GeneralString:username
45
46    [ ca ]
47    default_ca      = CA_default
48
49    [ CA_default ]
50    dir = cadir
51    database  = $dir/index.txt
52    crlnumber = $dir/crl.txt
53    default_md = sha1
54    default_days = 3600
55    default_crl_days = 3600
56    certificate = pycacert.pem
57    private_key = pycakey.pem
58    serial    = $dir/serial
59    RANDFILE  = $dir/.rand
60
61    policy          = policy_match
62
63    [ policy_match ]
64    countryName             = match
65    stateOrProvinceName     = optional
66    organizationName        = match
67    organizationalUnitName  = optional
68    commonName              = supplied
69    emailAddress            = optional
70
71    [ policy_anything ]
72    countryName   = optional
73    stateOrProvinceName = optional
74    localityName    = optional
75    organizationName  = optional
76    organizationalUnitName  = optional
77    commonName    = supplied
78    emailAddress    = optional
79
80
81    [ v3_ca ]
82
83    subjectKeyIdentifier=hash
84    authorityKeyIdentifier=keyid:always,issuer
85    basicConstraints = CA:true
86
87    """
88
89here = os.path.abspath(os.path.dirname(__file__))
90
91def make_cert_key(hostname, sign=False, extra_san=''):
92    print("creating cert for " + hostname)
93    tempnames = []
94    for i in range(3):
95        with tempfile.NamedTemporaryFile(delete=False) as f:
96            tempnames.append(f.name)
97    req_file, cert_file, key_file = tempnames
98    try:
99        req = req_template.format(hostname=hostname, extra_san=extra_san)
100        with open(req_file, 'w') as f:
101            f.write(req)
102        args = ['req', '-new', '-days', '3650', '-nodes',
103                '-newkey', 'rsa:1024', '-keyout', key_file,
104                '-config', req_file]
105        if sign:
106            with tempfile.NamedTemporaryFile(delete=False) as f:
107                tempnames.append(f.name)
108                reqfile = f.name
109            args += ['-out', reqfile ]
110
111        else:
112            args += ['-x509', '-out', cert_file ]
113        check_call(['openssl'] + args)
114
115        if sign:
116            args = ['ca', '-config', req_file, '-out', cert_file, '-outdir', 'cadir',
117                    '-policy', 'policy_anything', '-batch', '-infiles', reqfile ]
118            check_call(['openssl'] + args)
119
120
121        with open(cert_file, 'r') as f:
122            cert = f.read()
123        with open(key_file, 'r') as f:
124            key = f.read()
125        return cert, key
126    finally:
127        for name in tempnames:
128            os.remove(name)
129
130TMP_CADIR = 'cadir'
131
132def unmake_ca():
133    shutil.rmtree(TMP_CADIR)
134
135def make_ca():
136    os.mkdir(TMP_CADIR)
137    with open(os.path.join('cadir','index.txt'),'a+') as f:
138        pass # empty file
139    with open(os.path.join('cadir','crl.txt'),'a+') as f:
140        f.write("00")
141    with open(os.path.join('cadir','index.txt.attr'),'w+') as f:
142        f.write('unique_subject = no')
143
144    with tempfile.NamedTemporaryFile("w") as t:
145        t.write(req_template.format(hostname='our-ca-server', extra_san=''))
146        t.flush()
147        with tempfile.NamedTemporaryFile() as f:
148            args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes',
149                    '-newkey', 'rsa:2048', '-keyout', 'pycakey.pem',
150                    '-out', f.name,
151                    '-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=our-ca-server']
152            check_call(['openssl'] + args)
153            args = ['ca', '-config', t.name, '-create_serial',
154                    '-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR,
155                    '-keyfile', 'pycakey.pem', '-days', '3650',
156                    '-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ]
157            check_call(['openssl'] + args)
158            args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl']
159            check_call(['openssl'] + args)
160
161if __name__ == '__main__':
162    os.chdir(here)
163    cert, key = make_cert_key('localhost')
164    with open('ssl_cert.pem', 'w') as f:
165        f.write(cert)
166    with open('ssl_key.pem', 'w') as f:
167        f.write(key)
168    print("password protecting ssl_key.pem in ssl_key.passwd.pem")
169    check_call(['openssl','rsa','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-des3','-passout','pass:somepass'])
170    check_call(['openssl','rsa','-in','ssl_key.pem','-out','keycert.passwd.pem','-des3','-passout','pass:somepass'])
171
172    with open('keycert.pem', 'w') as f:
173        f.write(key)
174        f.write(cert)
175
176    with open('keycert.passwd.pem', 'a+') as f:
177        f.write(cert)
178
179    # For certificate matching tests
180    make_ca()
181    cert, key = make_cert_key('fakehostname')
182    with open('keycert2.pem', 'w') as f:
183        f.write(key)
184        f.write(cert)
185
186    cert, key = make_cert_key('localhost', True)
187    with open('keycert3.pem', 'w') as f:
188        f.write(key)
189        f.write(cert)
190
191    cert, key = make_cert_key('fakehostname', True)
192    with open('keycert4.pem', 'w') as f:
193        f.write(key)
194        f.write(cert)
195
196    extra_san = [
197        'otherName.1 = 1.2.3.4;UTF8:some other identifier',
198        'otherName.2 = 1.3.6.1.5.2.2;SEQUENCE:princ_name',
199        'email.1 = user@example.org',
200        'DNS.2 = www.example.org',
201        # GEN_X400
202        'dirName.1 = dir_sect',
203        # GEN_EDIPARTY
204        'URI.1 = https://www.python.org/',
205        'IP.1 = 127.0.0.1',
206        'IP.2 = ::1',
207        'RID.1 = 1.2.3.4.5',
208    ]
209
210    cert, key = make_cert_key('allsans', extra_san='\n'.join(extra_san))
211    with open('allsans.pem', 'w') as f:
212        f.write(key)
213        f.write(cert)
214
215    unmake_ca()
216    print("\n\nPlease change the values in test_ssl.py, test_parse_cert function related to notAfter,notBefore and serialNumber")
217    check_call(['openssl','x509','-in','keycert.pem','-dates','-serial','-noout'])
218