195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn#!/usr/bin/python 295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn"""Extract certificates from a multi-certificate pem file. 495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 595b0a57771b92fa238882ffbcf5ee377c8110f75Darren KrahnEach certificate in the file is extracted into a format appropriate for use with 695b0a57771b92fa238882ffbcf5ee377c8110f75Darren KrahnBrillo or Android. On success, the contents of the output directory match the 795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahninput file exactly. Existing files in the output directory will be deleted. 895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 995b0a57771b92fa238882ffbcf5ee377c8110f75Darren KrahnThe current date will be written into the timestamp file, './TIMESTAMP' by 1095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahndefault. 1195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 1295b0a57771b92fa238882ffbcf5ee377c8110f75Darren KrahnTypical usage (extracting from ./roots.pem and output into ./files): 1395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn> ./extract_from_pem.py 1495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn""" 1595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 1695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahnimport argparse 1795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahnimport datetime 1895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahnimport os 1995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahnimport re 2095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 2195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahnimport M2Crypto # sudo apt-get install python-m2crypto 2295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 2395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 2495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahndef WriteCertificateFile(content, base_name, output_dir): 2595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn """Writes a certificate file to the output directory. 2695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 2795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn Args: 2895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn content: The file content to write. 2995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn base_name: The file name will be base_name.n where n is the first available 3095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn non-negative integer. Ex. if myfile.0 exists and has different 3195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn content, the output file will be myfile.1. 3295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn output_dir: The output directory. 3395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn """ 3495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn i = 0 3595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn file_path = os.path.join(output_dir, '%s.%d' % (base_name, i)) 3695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn while os.path.exists(file_path): 3795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn with open(file_path) as existing_file: 3895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn if content == existing_file.read(): 3995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn # Ignore identical duplicate. 4095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn return 4195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn i += 1 4295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn file_path = os.path.join(output_dir, '%s.%d' % (base_name, i)) 4395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn with open(file_path, 'w') as new_file: 4495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn new_file.write(content) 4595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 4695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 4795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahndef GetFingerprintString(x509): 4895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn """Computes a fingerprint string as output by 'openssl x509 -fingerprint'. 4995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 5095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn Args: 5195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn x509: A M2Crypto.X509.X509 object. 5295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 5395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn Returns: 5495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn The fingerprint as a string. 5595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn """ 5695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn # Zero filled and with ':' between bytes. 5795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn return ':'.join(re.findall(r'..', x509.get_fingerprint('sha1').zfill(40))) 5895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 5995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 6095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahndef main(): 6195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn parser = argparse.ArgumentParser(description='PEM Certificate Importer') 6295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn parser.add_argument('--pem_file', nargs='?', default='roots.pem') 6395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn parser.add_argument('--output_dir', nargs='?', default='files') 6495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn parser.add_argument('--timestamp_file', nargs='?', default='TIMESTAMP') 6595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn args = parser.parse_args() 6695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn assert os.path.isdir(args.output_dir) and os.path.isfile(args.pem_file) 6795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn if 'y' != raw_input('All files in \'%s\' will be deleted. Proceed? [y,N]: ' % 6895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn args.output_dir): 6995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn print 'Aborted.' 7095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn return 7195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn for existing_file in os.listdir(args.output_dir): 7295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn os.remove(os.path.join(args.output_dir, existing_file)) 7395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn with open(args.pem_file) as pem_file: 7495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn pattern = r'-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----' 7595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn pem_certs = re.findall(pattern, pem_file.read()) 7695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn for pem_cert in pem_certs: 7795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn x509 = M2Crypto.X509.load_cert_string(pem_cert) 7895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn content = '%s%sSHA1 Fingerprint=%s\n' % (x509.as_pem(), 7995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn x509.as_text(), 8095b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn GetFingerprintString(x509)) 8195b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn base_name = '%08x' % x509.get_subject().as_hash() 8295b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn WriteCertificateFile(content, base_name, args.output_dir) 8395b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn with open(args.timestamp_file, 'w') as timestamp_file: 8495b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn timestamp_file.write('Last Update (YYYY-MM-DD): %s\n' % 8595b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn datetime.date.today().isoformat()) 8695b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 8795b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn 8895b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahnif __name__ == '__main__': 8995b0a57771b92fa238882ffbcf5ee377c8110f75Darren Krahn main() 90