OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2015 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2015 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Set of helpers to generate signed X.509v3 certificates. | 6 """Set of helpers to generate signed X.509v3 certificates. |
7 | 7 |
8 This works by shelling out calls to the 'openssl req' and 'openssl ca' | 8 This works by shelling out calls to the 'openssl req' and 'openssl ca' |
9 commands, and passing the appropriate command line flags and configuration file | 9 commands, and passing the appropriate command line flags and configuration file |
10 (.cnf). | 10 (.cnf). |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 | 71 |
72 class Certificate(object): | 72 class Certificate(object): |
73 """Helper for building an X.509 certificate.""" | 73 """Helper for building an X.509 certificate.""" |
74 | 74 |
75 def __init__(self, name, cert_type, issuer): | 75 def __init__(self, name, cert_type, issuer): |
76 # The name will be used for the subject's CN, and also as a component of | 76 # The name will be used for the subject's CN, and also as a component of |
77 # the temporary filenames to help with debugging. | 77 # the temporary filenames to help with debugging. |
78 self.name = name | 78 self.name = name |
79 self.path_id = GetUniquePathId(name) | 79 self.path_id = GetUniquePathId(name) |
80 | 80 |
| 81 # If specified, use the key from this path instead of generating a new one. |
| 82 self.key_path = None |
| 83 |
81 # The issuer is also a Certificate object. Passing |None| means it is a | 84 # The issuer is also a Certificate object. Passing |None| means it is a |
82 # self-signed certificate. | 85 # self-signed certificate. |
83 self.issuer = issuer | 86 self.issuer = issuer |
84 if issuer is None: | 87 if issuer is None: |
85 self.issuer = self | 88 self.issuer = self |
86 | 89 |
87 # The config contains all the OpenSSL options that will be passed via a | 90 # The config contains all the OpenSSL options that will be passed via a |
88 # .cnf file. Set up defaults. | 91 # .cnf file. Set up defaults. |
89 self.config = openssl_conf.Config() | 92 self.config = openssl_conf.Config() |
90 self.init_config() | 93 self.init_config() |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 | 128 |
126 # Initialize any files that will be needed if this certificate is used to | 129 # Initialize any files that will be needed if this certificate is used to |
127 # sign other certificates. Starts off serial numbers at 1, and will | 130 # sign other certificates. Starts off serial numbers at 1, and will |
128 # increment them for each signed certificate. | 131 # increment them for each signed certificate. |
129 write_string_to_file('01\n', self.get_serial_path()) | 132 write_string_to_file('01\n', self.get_serial_path()) |
130 write_string_to_file('', self.get_database_path()) | 133 write_string_to_file('', self.get_database_path()) |
131 | 134 |
132 | 135 |
133 def generate_rsa_key(self, size_bits): | 136 def generate_rsa_key(self, size_bits): |
134 """Generates an RSA private key for the certificate.""" | 137 """Generates an RSA private key for the certificate.""" |
| 138 assert self.key_path is None |
135 subprocess.check_call( | 139 subprocess.check_call( |
136 ['openssl', 'genrsa', '-out', self.get_key_path(), str(size_bits)]) | 140 ['openssl', 'genrsa', '-out', self.get_key_path(), str(size_bits)]) |
137 | 141 |
138 | 142 |
139 def generate_ec_key(self, named_curve): | 143 def generate_ec_key(self, named_curve): |
140 """Generates an EC private key for the certificate. |named_curve| can be | 144 """Generates an EC private key for the certificate. |named_curve| can be |
141 something like secp384r1""" | 145 something like secp384r1""" |
| 146 assert self.key_path is None |
142 subprocess.check_call( | 147 subprocess.check_call( |
143 ['openssl', 'ecparam', '-out', self.get_key_path(), | 148 ['openssl', 'ecparam', '-out', self.get_key_path(), |
144 '-name', named_curve, '-genkey']) | 149 '-name', named_curve, '-genkey']) |
145 | 150 |
146 | 151 |
147 def set_validity_range(self, start_date, end_date): | 152 def set_validity_range(self, start_date, end_date): |
148 """Sets the Validity notBefore and notAfter properties for the | 153 """Sets the Validity notBefore and notAfter properties for the |
149 certificate""" | 154 certificate""" |
150 self.validity_flags = ['-startdate', start_date, '-enddate', end_date] | 155 self.validity_flags = ['-startdate', start_date, '-enddate', end_date] |
151 | 156 |
152 | 157 |
153 def set_signature_hash(self, md): | 158 def set_signature_hash(self, md): |
154 """Sets the hash function that will be used when signing this certificate. | 159 """Sets the hash function that will be used when signing this certificate. |
155 Can be sha1, sha256, sha512, md5, etc.""" | 160 Can be sha1, sha256, sha512, md5, etc.""" |
156 self.md_flags = ['-md', md] | 161 self.md_flags = ['-md', md] |
157 | 162 |
158 | 163 |
159 def get_extensions(self): | 164 def get_extensions(self): |
160 return self.config.get_section('req_ext') | 165 return self.config.get_section('req_ext') |
161 | 166 |
162 | 167 |
163 def get_path(self, suffix): | 168 def get_path(self, suffix): |
164 """Forms a path to an output file for this certificate, containing the | 169 """Forms a path to an output file for this certificate, containing the |
165 indicated suffix. The certificate's name will be used as its basis.""" | 170 indicated suffix. The certificate's name will be used as its basis.""" |
166 return os.path.join(g_out_dir, '%s%s' % (self.path_id, suffix)) | 171 return os.path.join(g_out_dir, '%s%s' % (self.path_id, suffix)) |
167 | 172 |
168 | 173 |
| 174 def set_key_path(self, path): |
| 175 """Uses the key from the given path instead of generating a new one.""" |
| 176 self.key_path = path |
| 177 section = self.config.get_section('root_ca') |
| 178 section.set_property('private_key', self.get_key_path()) |
| 179 |
| 180 |
169 def get_key_path(self): | 181 def get_key_path(self): |
| 182 if self.key_path is not None: |
| 183 return self.key_path |
170 return self.get_path('.key') | 184 return self.get_path('.key') |
171 | 185 |
172 | 186 |
173 def get_cert_path(self): | 187 def get_cert_path(self): |
174 return self.get_path('.pem') | 188 return self.get_path('.pem') |
175 | 189 |
176 | 190 |
177 def get_serial_path(self): | 191 def get_serial_path(self): |
178 return self.get_path('.serial') | 192 return self.get_path('.serial') |
179 | 193 |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
344 section = self.config.get_section('crl_ext') | 358 section = self.config.get_section('crl_ext') |
345 section.set_property('authorityKeyIdentifier', 'keyid:always') | 359 section.set_property('authorityKeyIdentifier', 'keyid:always') |
346 section.set_property('authorityInfoAccess', '@issuer_info') | 360 section.set_property('authorityInfoAccess', '@issuer_info') |
347 | 361 |
348 | 362 |
349 def data_to_pem(block_header, block_data): | 363 def data_to_pem(block_header, block_data): |
350 return '-----BEGIN %s-----\n%s\n-----END %s-----\n' % (block_header, | 364 return '-----BEGIN %s-----\n%s\n-----END %s-----\n' % (block_header, |
351 base64.b64encode(block_data), block_header) | 365 base64.b64encode(block_data), block_header) |
352 | 366 |
353 | 367 |
354 def write_test_file(description, chain, trusted_certs, utc_time, verify_result): | 368 def write_test_file(description, chain, trusted_certs, utc_time, verify_result, |
| 369 out_pem=None): |
355 """Writes a test file that contains all the inputs necessary to run a | 370 """Writes a test file that contains all the inputs necessary to run a |
356 verification on a certificate chain""" | 371 verification on a certificate chain""" |
357 | 372 |
358 # Prepend the script name that generated the file to the description. | 373 # Prepend the script name that generated the file to the description. |
359 test_data = '[Created by: %s]\n\n%s\n' % (sys.argv[0], description) | 374 test_data = '[Created by: %s]\n\n%s\n' % (sys.argv[0], description) |
360 | 375 |
361 # Write the certificate chain to the output file. | 376 # Write the certificate chain to the output file. |
362 for cert in chain: | 377 for cert in chain: |
363 test_data += '\n' + cert.get_cert_pem() | 378 test_data += '\n' + cert.get_cert_pem() |
364 | 379 |
365 # Write the trust store. | 380 # Write the trust store. |
366 for cert in trusted_certs: | 381 for cert in trusted_certs: |
367 cert_data = cert.get_cert_pem() | 382 cert_data = cert.get_cert_pem() |
368 # Use a different block type in the .pem file. | 383 # Use a different block type in the .pem file. |
369 cert_data = cert_data.replace('CERTIFICATE', 'TRUSTED_CERTIFICATE') | 384 cert_data = cert_data.replace('CERTIFICATE', 'TRUSTED_CERTIFICATE') |
370 test_data += '\n' + cert_data | 385 test_data += '\n' + cert_data |
371 | 386 |
372 test_data += '\n' + data_to_pem('TIME', utc_time) | 387 test_data += '\n' + data_to_pem('TIME', utc_time) |
373 | 388 |
374 verify_result_string = 'SUCCESS' if verify_result else 'FAIL' | 389 verify_result_string = 'SUCCESS' if verify_result else 'FAIL' |
375 test_data += '\n' + data_to_pem('VERIFY_RESULT', verify_result_string) | 390 test_data += '\n' + data_to_pem('VERIFY_RESULT', verify_result_string) |
376 | 391 |
377 write_string_to_file(test_data, g_out_pem) | 392 write_string_to_file(test_data, out_pem if out_pem else g_out_pem) |
378 | 393 |
379 | 394 |
380 def write_string_to_file(data, path): | 395 def write_string_to_file(data, path): |
381 with open(path, 'w') as f: | 396 with open(path, 'w') as f: |
382 f.write(data) | 397 f.write(data) |
383 | 398 |
384 | 399 |
385 def init(invoking_script_path): | 400 def init(invoking_script_path): |
386 """Creates an output directory to contain all the temporary files that may be | 401 """Creates an output directory to contain all the temporary files that may be |
387 created, as well as determining the path for the final output. These paths | 402 created, as well as determining the path for the final output. These paths |
(...skipping 26 matching lines...) Expand all Loading... |
414 | 429 |
415 | 430 |
416 def create_intermediary_certificate(name, issuer): | 431 def create_intermediary_certificate(name, issuer): |
417 return Certificate(name, TYPE_CA, issuer) | 432 return Certificate(name, TYPE_CA, issuer) |
418 | 433 |
419 | 434 |
420 def create_end_entity_certificate(name, issuer): | 435 def create_end_entity_certificate(name, issuer): |
421 return Certificate(name, TYPE_END_ENTITY, issuer) | 436 return Certificate(name, TYPE_END_ENTITY, issuer) |
422 | 437 |
423 init(sys.argv[0]) | 438 init(sys.argv[0]) |
OLD | NEW |