1#!/usr/bin/env python 2# Copyright 2016 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6""" 7Given a path to a XXX.pem file, re-generates a CERTIFICATE. 8 9The .pem file is expected to contain comments that resemble: 10 11#-----BEGIN XXX----- 12<ascii-der values in here> 13#-----END XXX----- 14 15These are interpreted as substitutions to make inside of the Certificate 16template (v3_certificate_template.txt) 17""" 18 19import sys 20import os 21import re 22import base64 23import subprocess 24 25 26def read_file_to_string(path): 27 """Reads a file entirely to a string""" 28 with open(path, 'r') as f: 29 return f.read() 30 31 32def write_string_to_file(data, path): 33 """Writes a string to a file""" 34 print "Writing file %s ..." % (path) 35 with open(path, "w") as f: 36 f.write(data) 37 38 39def replace_string(original, start, end, replacement): 40 """Replaces the specified range of |original| with |replacement|""" 41 return original[0:start] + replacement + original[end:] 42 43 44def apply_substitution(template, name, value): 45 """Finds a section named |name| in |template| and replaces it with |value|.""" 46 # Find the section |name| in |template|. 47 regex = re.compile(r'#-----BEGIN %s-----(.*?)#-----END %s-----' % 48 (re.escape(name), re.escape(name)), re.DOTALL) 49 m = regex.search(template) 50 if not m: 51 print "Couldn't find a section named %s in the template" % (name) 52 sys.exit(1) 53 54 return replace_string(template, m.start(1), m.end(1), value) 55 56 57def main(): 58 if len(sys.argv) != 2: 59 print 'Usage: %s <PATH_TO_PEM>' % (sys.argv[0]) 60 sys.exit(1) 61 62 pem_path = sys.argv[1] 63 orig = read_file_to_string(pem_path) 64 65 cert_ascii = read_file_to_string("v3_certificate_template.txt") 66 67 # Apply all substitutions described by comments in |orig| 68 regex = re.compile(r'#-----BEGIN ([\w ]+)-----(.*?)#-----END \1-----', 69 re.DOTALL) 70 num_matches = 0 71 for m in regex.finditer(orig): 72 num_matches += 1 73 cert_ascii = apply_substitution(cert_ascii, m.group(1), m.group(2)) 74 75 if num_matches == 0: 76 print "Input did not contain any substitutions" 77 sys.exit(1) 78 79 # Convert the ascii-der to actual DER binary. 80 cert_der = None 81 try: 82 p = subprocess.Popen(['ascii2der'], stdout=subprocess.PIPE, 83 stdin=subprocess.PIPE, stderr=subprocess.STDOUT) 84 cert_der = p.communicate(input=cert_ascii)[0] 85 except OSError as e: 86 print ('ERROR: Failed executing ascii2der.\n' 87 'Make sure this is in your path\n' 88 'Obtain it from https://github.com/google/der-ascii') 89 sys.exit(1) 90 91 # Replace the CERTIFICATE block with the newly generated one. 92 regex = re.compile(r'-----BEGIN CERTIFICATE-----\n(.*?)\n' 93 '-----END CERTIFICATE-----', re.DOTALL) 94 m = regex.search(orig) 95 if not m: 96 print "ERROR: Cannot find CERTIFICATE block in input" 97 sys.exit(1) 98 modified = replace_string(orig, m.start(1), m.end(1), 99 base64.b64encode(cert_der)) 100 101 # Write back the .pem file. 102 write_string_to_file(modified, pem_path) 103 104main() 105