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