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