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 13Usage: 14generate_sslroots.py cert_file.crt [--verbose | -v] [--full_cert | -f] 15 16Arguments: 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 22import commands 23from optparse import OptionParser 24import os 25import re 26import 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 43def 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 57def _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 85def _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 125def _Cleanup(root_dir): 126 for f in os.listdir(root_dir): 127 if f.startswith(_PREFIX): 128 os.remove(root_dir + f) 129 130 131def _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 153def _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 162def _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 168def _AddLabelToArray(label, type_name): 169 return ' %s_%s,\n' %(label, type_name) 170 171 172def _CreateArraySectionFooter(): 173 return '};\n\n' 174 175 176def _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 185def _PrintOutput(output, options): 186 if options.verbose: 187 print output 188 189if __name__ == '__main__': 190 main() 191