2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
1 |
#! /usr/bin/env python
|
2 |
||
3 |
# Copyright (C) 2007 Canonical Ltd
|
|
4 |
#
|
|
5 |
# This program is free software; you can redistribute it and/or modify
|
|
6 |
# it under the terms of the GNU General Public License as published by
|
|
7 |
# the Free Software Foundation; either version 2 of the License, or
|
|
8 |
# (at your option) any later version.
|
|
9 |
#
|
|
10 |
# This program is distributed in the hope that it will be useful,
|
|
11 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13 |
# GNU General Public License for more details.
|
|
14 |
#
|
|
15 |
# You should have received a copy of the GNU General Public License
|
|
16 |
# along with this program; if not, write to the Free Software
|
|
4183.7.1
by Sabin Iacob
update FSF mailing address |
17 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
18 |
|
19 |
"""create_ssls.py -- create sll keys and certificates for tests.
|
|
20 |
||
21 |
The https server requires at least a key and a certificate to start.
|
|
22 |
||
23 |
SSL keys and certificates are created with openssl which may not be available
|
|
24 |
everywhere we want to run the test suite.
|
|
25 |
||
26 |
To simplify test writing, the necessary keys and certificates are generated by
|
|
27 |
this script and used by the tests.
|
|
28 |
||
29 |
Since creating these test keys and certificates requires a good knowledge of
|
|
30 |
openssl and a lot of typing, we record all the needed parameters here.
|
|
31 |
||
32 |
Since this will be used rarely, no effort has been made to handle exotic
|
|
33 |
errors, the basic policy is that openssl should be available in the path and
|
|
34 |
the parameters should be correct, any error will abort the script. Feel free to
|
|
35 |
enhance that.
|
|
36 |
||
37 |
This script provides options for building any individual files or two options
|
|
38 |
to build the certificate authority files (--ca) or the server files (--server).
|
|
39 |
"""
|
|
40 |
||
41 |
from cStringIO import StringIO |
|
42 |
import optparse |
|
43 |
import os |
|
44 |
from subprocess import ( |
|
45 |
CalledProcessError, |
|
46 |
Popen, |
|
47 |
PIPE, |
|
48 |
)
|
|
4830.2.1
by Vincent Ladeuil
Fix https tests regression. |
49 |
import sys |
50 |
||
51 |
# We want to use the right bzrlib: the one we are part of
|
|
52 |
# FIXME: The fllowing is correct but looks a bit ugly
|
|
53 |
_dir = os.path.dirname |
|
54 |
our_bzr = _dir(_dir(_dir(_dir(os.path.realpath(__file__))))) |
|
55 |
sys.path.insert(0, our_bzr) |
|
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
56 |
|
57 |
from bzrlib import ( |
|
58 |
osutils, |
|
59 |
)
|
|
60 |
from bzrlib.tests import ( |
|
61 |
ssl_certs, |
|
62 |
)
|
|
63 |
||
64 |
def error(s): |
|
65 |
print s |
|
66 |
exit(1) |
|
67 |
||
68 |
def needs(request, *paths): |
|
69 |
"""Errors out if the specified path does not exists"""
|
|
70 |
missing = [p for p in paths if not os.path.exists(p)] |
|
71 |
if missing: |
|
72 |
error('%s needs: %s' % (request, ','.join(missing))) |
|
73 |
||
74 |
||
75 |
def rm_f(path): |
|
76 |
"""rm -f path"""
|
|
77 |
try: |
|
78 |
os.unlink(path) |
|
79 |
except: |
|
80 |
pass
|
|
81 |
||
82 |
def _openssl(args, input=None): |
|
83 |
"""Execute a command in a subproces feeding stdin with the provided input.
|
|
84 |
||
85 |
:return: (returncode, stdout, stderr)
|
|
86 |
"""
|
|
87 |
cmd = ['openssl'] + args |
|
88 |
proc = Popen(cmd, stdin=PIPE) |
|
89 |
(stdout, stderr) = proc.communicate(input) |
|
90 |
if proc.returncode: |
|
91 |
# Basic error handling, all commands should succeed
|
|
92 |
raise CalledProcessError(proc.returncode, cmd) |
|
93 |
return proc.returncode, stdout, stderr |
|
94 |
||
95 |
||
96 |
ssl_params=dict( |
|
97 |
# Passwords
|
|
98 |
server_pass='I will protect the communications', |
|
99 |
server_challenge_pass='Challenge for the CA', |
|
100 |
ca_pass='I am the authority for the whole... localhost', |
|
101 |
# CA identity
|
|
102 |
ca_country_code='BZ', |
|
103 |
ca_state='Internet', |
|
104 |
ca_locality='Bazaar', |
|
105 |
ca_organization='Distributed', |
|
106 |
ca_section='VCS', |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
107 |
ca_name='Master of certificates', |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
108 |
ca_email='cert@no.spam', |
109 |
# Server identity
|
|
110 |
server_country_code='LH', |
|
111 |
server_state='Internet', |
|
112 |
server_locality='LocalHost', |
|
113 |
server_organization='Testing Ltd', |
|
114 |
server_section='https server', |
|
4830.2.1
by Vincent Ladeuil
Fix https tests regression. |
115 |
server_name='127.0.0.1', # Always accessed under that name |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
116 |
server_email='https_server@locahost', |
117 |
server_optional_company_name='', |
|
118 |
)
|
|
119 |
||
120 |
||
121 |
def build_ca_key(): |
|
122 |
"""Generate an ssl certificate authority private key."""
|
|
123 |
key_path = ssl_certs.build_path('ca.key') |
|
124 |
rm_f(key_path) |
|
125 |
_openssl(['genrsa', '-passout', 'stdin', '-des3', '-out', key_path, '4096'], |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
126 |
input='%(ca_pass)s\n%(ca_pass)s\n' % ssl_params) |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
127 |
|
128 |
||
129 |
def build_ca_certificate(): |
|
130 |
"""Generate an ssl certificate authority private key."""
|
|
131 |
key_path = ssl_certs.build_path('ca.key') |
|
132 |
needs('Building ca.crt', key_path) |
|
133 |
cert_path = ssl_certs.build_path('ca.crt') |
|
134 |
rm_f(cert_path) |
|
135 |
_openssl(['req', '-passin', 'stdin', '-new', '-x509', |
|
136 |
# Will need to be generated again in 10 years -- vila 20071122
|
|
137 |
'-days', '3650', |
|
138 |
'-key', key_path, '-out', cert_path], |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
139 |
input='%(ca_pass)s\n' |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
140 |
'%(ca_country_code)s\n' |
141 |
'%(ca_state)s\n' |
|
142 |
'%(ca_locality)s\n' |
|
143 |
'%(ca_organization)s\n' |
|
144 |
'%(ca_section)s\n' |
|
145 |
'%(ca_name)s\n' |
|
146 |
'%(ca_email)s\n' |
|
147 |
% ssl_params) |
|
148 |
||
149 |
||
150 |
def build_server_key(): |
|
151 |
"""Generate an ssl server private key.
|
|
152 |
||
153 |
We generates a key with a password and then copy it without password so
|
|
154 |
that as server can user it without prompting.
|
|
155 |
"""
|
|
156 |
key_path = ssl_certs.build_path('server_with_pass.key') |
|
157 |
rm_f(key_path) |
|
158 |
_openssl(['genrsa', '-passout', 'stdin', '-des3', '-out', key_path, '4096'], |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
159 |
input='%(server_pass)s\n%(server_pass)s\n' % ssl_params) |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
160 |
|
161 |
key_nopass_path = ssl_certs.build_path('server_without_pass.key') |
|
162 |
rm_f(key_nopass_path) |
|
163 |
_openssl(['rsa', '-passin', 'stdin', '-in', key_path, |
|
164 |
'-out', key_nopass_path,], |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
165 |
input='%(server_pass)s\n' % ssl_params) |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
166 |
|
167 |
||
168 |
def build_server_signing_request(): |
|
169 |
"""Create a CSR (certificate signing request) to get signed by the CA"""
|
|
170 |
key_path = ssl_certs.build_path('server_with_pass.key') |
|
171 |
needs('Building server.csr', key_path) |
|
172 |
server_csr_path = ssl_certs.build_path('server.csr') |
|
173 |
rm_f(server_csr_path) |
|
174 |
_openssl(['req', '-passin', 'stdin', '-new', '-key', key_path, |
|
175 |
'-out', server_csr_path], |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
176 |
input='%(server_pass)s\n' |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
177 |
'%(server_country_code)s\n' |
178 |
'%(server_state)s\n' |
|
179 |
'%(server_locality)s\n' |
|
180 |
'%(server_organization)s\n' |
|
181 |
'%(server_section)s\n' |
|
182 |
'%(server_name)s\n' |
|
183 |
'%(server_email)s\n' |
|
184 |
'%(server_challenge_pass)s\n' |
|
185 |
'%(server_optional_company_name)s\n' |
|
186 |
% ssl_params) |
|
187 |
||
188 |
||
189 |
def sign_server_certificate(): |
|
190 |
"""CA signs server csr"""
|
|
191 |
server_csr_path = ssl_certs.build_path('server.csr') |
|
192 |
ca_cert_path = ssl_certs.build_path('ca.crt') |
|
193 |
ca_key_path = ssl_certs.build_path('ca.key') |
|
194 |
needs('Signing server.crt', server_csr_path, ca_cert_path, ca_key_path) |
|
195 |
server_cert_path = ssl_certs.build_path('server.crt') |
|
196 |
rm_f(server_cert_path) |
|
197 |
_openssl(['x509', '-req', '-passin', 'stdin', |
|
198 |
# Will need to be generated again in 10 years -- vila 20071122
|
|
199 |
'-days', '3650', |
|
200 |
'-in', server_csr_path, |
|
201 |
'-CA', ca_cert_path, '-CAkey', ca_key_path, |
|
202 |
'-set_serial', '01', |
|
203 |
'-out', server_cert_path,], |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
204 |
input='%(ca_pass)s\n' % ssl_params) |
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
205 |
|
206 |
||
207 |
def build_ssls(name, options, builders): |
|
208 |
if options is not None: |
|
209 |
for item in options: |
|
210 |
builder = builders.get(item, None) |
|
211 |
if builder is None: |
|
212 |
error('%s is not a known %s' % (item, name)) |
|
213 |
builder() |
|
214 |
||
215 |
||
216 |
opt_parser = optparse.OptionParser(usage="usage: %prog [options]") |
|
217 |
opt_parser.set_defaults(ca=False) |
|
218 |
opt_parser.set_defaults(server=False) |
|
219 |
opt_parser.add_option( |
|
220 |
"--ca", dest="ca", action="store_true", |
|
221 |
help="Generate CA key and certificate") |
|
222 |
opt_parser.add_option( |
|
223 |
"--server", dest="server", action="store_true", |
|
224 |
help="Generate server key, certificate signing request and certificate") |
|
225 |
opt_parser.add_option( |
|
226 |
"-k", "--key", dest="keys", action="append", metavar="KEY", |
|
227 |
help="generate a new KEY (several -k options can be specified)") |
|
228 |
opt_parser.add_option( |
|
229 |
"-c", "--certificate", dest="certificates", action="append", |
|
230 |
metavar="CERTIFICATE", |
|
231 |
help="generate a new CERTIFICATE (several -c options can be specified)") |
|
232 |
opt_parser.add_option( |
|
233 |
"-r", "--sign-request", dest="signing_requests", action="append", |
|
234 |
metavar="REQUEST", |
|
235 |
help="generate a new signing REQUEST (several -r options can be specified)") |
|
236 |
opt_parser.add_option( |
|
237 |
"-s", "--sign", dest="signings", action="append", |
|
238 |
metavar="SIGNING", |
|
239 |
help="generate a new SIGNING (several -s options can be specified)") |
|
240 |
||
241 |
||
242 |
key_builders = dict(ca=build_ca_key, server=build_server_key,) |
|
243 |
certificate_builders = dict(ca=build_ca_certificate,) |
|
244 |
signing_request_builders = dict(server=build_server_signing_request,) |
|
245 |
signing_builders = dict(server=sign_server_certificate,) |
|
246 |
||
247 |
||
248 |
if __name__ == '__main__': |
|
249 |
(Options, args) = opt_parser.parse_args() |
|
250 |
if (Options.ca or Options.server): |
|
251 |
if (Options.keys or Options.certificates or Options.signing_requests |
|
252 |
or Options.signings): |
|
253 |
error("--ca and --server can't be used with other options") |
|
2929.3.13
by Vincent Ladeuil
Update ssl generated files. Put the branch on the backburner until the ssl python module is fixed (bugs pending). |
254 |
# Handles --ca before --server so that both can be used in the same run
|
255 |
# to generate all the files needed by the https test server
|
|
2929.3.11
by Vincent Ladeuil
Ssl files needed for the test https server. |
256 |
if Options.ca: |
257 |
build_ca_key() |
|
258 |
build_ca_certificate() |
|
259 |
if Options.server: |
|
260 |
build_server_key() |
|
261 |
build_server_signing_request() |
|
262 |
sign_server_certificate() |
|
263 |
else: |
|
264 |
build_ssls('key', Options.keys, key_builders) |
|
265 |
build_ssls('certificate', Options.certificates, certificate_builders) |
|
266 |
build_ssls('signing request', Options.signing_requests, |
|
267 |
signing_request_builders) |
|
268 |
build_ssls('signing', Options.signings, signing_builders) |