1 # -*- coding:utf-8 -*- 2 # Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 # 4 # Use of this source code is governed by a BSD-style license 5 # that can be found in the LICENSE file in the root of the source 6 # tree. An additional intellectual property rights grant can be found 7 # in the file PATENTS. All contributing project authors may 8 # be found in the AUTHORS file in the root of the source tree. 9 10 11 """This is a tool to transform a crt file into a C/C++ header. 12 13 Usage: 14 generate_sslroots.py cert_file.crt [--verbose | -v] [--full_cert | -f] 15 16 Arguments: 17 -v Print output while running. 18 -f Add public key and certificate name. Default is to skip and reduce 19 generated file size. 20 """ 21 22 import commands 23 from optparse import OptionParser 24 import os 25 import re 26 import string 27 28 _GENERATED_FILE = 'sslroots.h' 29 _PREFIX = '__generated__' 30 _EXTENSION = '.crt' 31 _SUBJECT_NAME_ARRAY = 'subject_name' 32 _SUBJECT_NAME_VARIABLE = 'SubjectName' 33 _PUBLIC_KEY_ARRAY = 'public_key' 34 _PUBLIC_KEY_VARIABLE = 'PublicKey' 35 _CERTIFICATE_ARRAY = 'certificate' 36 _CERTIFICATE_VARIABLE = 'Certificate' 37 _CERTIFICATE_SIZE_VARIABLE = 'CertificateSize' 38 _INT_TYPE = 'size_t' 39 _CHAR_TYPE = 'const unsigned char*' 40 _VERBOSE = 'verbose' 41 42 43 def main(): 44 """The main entrypoint.""" 45 parser = OptionParser('usage %prog FILE') 46 parser.add_option('-v', '--verbose', dest='verbose', action='store_true') 47 parser.add_option('-f', '--full_cert', dest='full_cert', action='store_true') 48 options, args = parser.parse_args() 49 if len(args) < 1: 50 parser.error('No crt file specified.') 51 return 52 root_dir = _SplitCrt(args[0], options) 53 _GenCFiles(root_dir, options) 54 _Cleanup(root_dir) 55 56 57 def _SplitCrt(source_file, options): 58 sub_file_blocks = [] 59 label_name = '' 60 root_dir = os.path.dirname(os.path.abspath(source_file)) + '/' 61 _PrintOutput(root_dir, options) 62 f = open(source_file) 63 for line in f: 64 if line.startswith('# Label: '): 65 sub_file_blocks.append(line) 66 label = re.search(r'\".*\"', line) 67 temp_label = label.group(0) 68 end = len(temp_label)-1 69 label_name = _SafeName(temp_label[1:end]) 70 elif line.startswith('-----END CERTIFICATE-----'): 71 sub_file_blocks.append(line) 72 new_file_name = root_dir + _PREFIX + label_name + _EXTENSION 73 _PrintOutput('Generating: ' + new_file_name, options) 74 new_file = open(new_file_name, 'w') 75 for out_line in sub_file_blocks: 76 new_file.write(out_line) 77 new_file.close() 78 sub_file_blocks = [] 79 else: 80 sub_file_blocks.append(line) 81 f.close() 82 return root_dir 83 84 85 def _GenCFiles(root_dir, options): 86 output_header_file = open(root_dir + _GENERATED_FILE, 'w') 87 output_header_file.write(_CreateOutputHeader()) 88 if options.full_cert: 89 subject_name_list = _CreateArraySectionHeader(_SUBJECT_NAME_VARIABLE, 90 _CHAR_TYPE, options) 91 public_key_list = _CreateArraySectionHeader(_PUBLIC_KEY_VARIABLE, 92 _CHAR_TYPE, options) 93 certificate_list = _CreateArraySectionHeader(_CERTIFICATE_VARIABLE, 94 _CHAR_TYPE, options) 95 certificate_size_list = _CreateArraySectionHeader(_CERTIFICATE_SIZE_VARIABLE, 96 _INT_TYPE, options) 97 98 for _, _, files in os.walk(root_dir): 99 for current_file in files: 100 if current_file.startswith(_PREFIX): 101 prefix_length = len(_PREFIX) 102 length = len(current_file) - len(_EXTENSION) 103 label = current_file[prefix_length:length] 104 filtered_output, cert_size = _CreateCertSection(root_dir, current_file, 105 label, options) 106 output_header_file.write(filtered_output + '\n\n\n') 107 if options.full_cert: 108 subject_name_list += _AddLabelToArray(label, _SUBJECT_NAME_ARRAY) 109 public_key_list += _AddLabelToArray(label, _PUBLIC_KEY_ARRAY) 110 certificate_list += _AddLabelToArray(label, _CERTIFICATE_ARRAY) 111 certificate_size_list += (' %s,\n') %(cert_size) 112 113 if options.full_cert: 114 subject_name_list += _CreateArraySectionFooter() 115 output_header_file.write(subject_name_list) 116 public_key_list += _CreateArraySectionFooter() 117 output_header_file.write(public_key_list) 118 certificate_list += _CreateArraySectionFooter() 119 output_header_file.write(certificate_list) 120 certificate_size_list += _CreateArraySectionFooter() 121 output_header_file.write(certificate_size_list) 122 output_header_file.close() 123 124 125 def _Cleanup(root_dir): 126 for f in os.listdir(root_dir): 127 if f.startswith(_PREFIX): 128 os.remove(root_dir + f) 129 130 131 def _CreateCertSection(root_dir, source_file, label, options): 132 command = 'openssl x509 -in %s%s -noout -C' %(root_dir, source_file) 133 _PrintOutput(command, options) 134 output = commands.getstatusoutput(command)[1] 135 renamed_output = output.replace('unsigned char XXX_', 136 'const unsigned char ' + label + '_') 137 filtered_output = '' 138 cert_block = '^const unsigned char.*?};$' 139 prog = re.compile(cert_block, re.IGNORECASE | re.MULTILINE | re.DOTALL) 140 if not options.full_cert: 141 filtered_output = prog.sub('', renamed_output, count=2) 142 else: 143 filtered_output = renamed_output 144 145 cert_size_block = r'\d\d\d+' 146 prog2 = re.compile(cert_size_block, re.MULTILINE | re.VERBOSE) 147 result = prog2.findall(renamed_output) 148 cert_size = result[len(result) - 1] 149 150 return filtered_output, cert_size 151 152 153 def _CreateOutputHeader(): 154 output = ('// This file is the root certificates in C form that are needed to' 155 ' connect to\n// Google.\n\n' 156 '// It was generated with the following command line:\n' 157 '// > python tools/certs/generate_sslroots.py' 158 '\n// https://pki.google.com/roots.pem\n\n') 159 return output 160 161 162 def _CreateArraySectionHeader(type_name, type_type, options): 163 output = ('const %s kSSLCert%sList[] = {\n') %(type_type, type_name) 164 _PrintOutput(output, options) 165 return output 166 167 168 def _AddLabelToArray(label, type_name): 169 return ' %s_%s,\n' %(label, type_name) 170 171 172 def _CreateArraySectionFooter(): 173 return '};\n\n' 174 175 176 def _SafeName(original_file_name): 177 bad_chars = ' -./\\()' 178 replacement_chars = '' 179 for _ in bad_chars: 180 replacement_chars += '_' 181 translation_table = string.maketrans(bad_chars, replacement_chars) 182 return original_file_name.translate(translation_table) 183 184 185 def _PrintOutput(output, options): 186 if options.verbose: 187 print output 188 189 if __name__ == '__main__': 190 main() 191