| 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 |