1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2017 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard Worker"""Pretty-prints certificates as an openssl-annotated PEM file.""" 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard Workerimport argparse 9*6777b538SAndroid Build Coastguard Workerimport base64 10*6777b538SAndroid Build Coastguard Workerimport errno 11*6777b538SAndroid Build Coastguard Workerimport hashlib 12*6777b538SAndroid Build Coastguard Workerimport os 13*6777b538SAndroid Build Coastguard Workerimport re 14*6777b538SAndroid Build Coastguard Workerimport subprocess 15*6777b538SAndroid Build Coastguard Workerimport sys 16*6777b538SAndroid Build Coastguard Workerimport traceback 17*6777b538SAndroid Build Coastguard Worker 18*6777b538SAndroid Build Coastguard Worker 19*6777b538SAndroid Build Coastguard Workerdef read_file_to_string(path): 20*6777b538SAndroid Build Coastguard Worker with open(path, 'rb') as f: 21*6777b538SAndroid Build Coastguard Worker return f.read() 22*6777b538SAndroid Build Coastguard Worker 23*6777b538SAndroid Build Coastguard Worker 24*6777b538SAndroid Build Coastguard Workerdef read_certificates_data_from_server(hostname): 25*6777b538SAndroid Build Coastguard Worker """Uses openssl to fetch the PEM-encoded certificates for an SSL server.""" 26*6777b538SAndroid Build Coastguard Worker p = subprocess.Popen(["openssl", "s_client", "-showcerts", 27*6777b538SAndroid Build Coastguard Worker "-servername", hostname, 28*6777b538SAndroid Build Coastguard Worker "-connect", hostname + ":443"], 29*6777b538SAndroid Build Coastguard Worker stdin=subprocess.PIPE, 30*6777b538SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 31*6777b538SAndroid Build Coastguard Worker stderr=subprocess.PIPE) 32*6777b538SAndroid Build Coastguard Worker result = p.communicate() 33*6777b538SAndroid Build Coastguard Worker 34*6777b538SAndroid Build Coastguard Worker if p.returncode == 0: 35*6777b538SAndroid Build Coastguard Worker return result[0] 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Worker sys.stderr.write("Failed getting certificates for %s:\n%s\n" % ( 38*6777b538SAndroid Build Coastguard Worker hostname, result[1])) 39*6777b538SAndroid Build Coastguard Worker return b"" 40*6777b538SAndroid Build Coastguard Worker 41*6777b538SAndroid Build Coastguard Worker 42*6777b538SAndroid Build Coastguard Workerdef read_sources_from_commandline(sources): 43*6777b538SAndroid Build Coastguard Worker """Processes the command lines and returns an array of all the sources 44*6777b538SAndroid Build Coastguard Worker bytes.""" 45*6777b538SAndroid Build Coastguard Worker sources_bytes = [] 46*6777b538SAndroid Build Coastguard Worker 47*6777b538SAndroid Build Coastguard Worker if not sources: 48*6777b538SAndroid Build Coastguard Worker # If no command-line arguments were given to the program, read input from 49*6777b538SAndroid Build Coastguard Worker # stdin. 50*6777b538SAndroid Build Coastguard Worker sources_bytes.append(sys.stdin.buffer.read()) 51*6777b538SAndroid Build Coastguard Worker else: 52*6777b538SAndroid Build Coastguard Worker for arg in sources: 53*6777b538SAndroid Build Coastguard Worker # If the argument identifies a file path, read it 54*6777b538SAndroid Build Coastguard Worker if os.path.exists(arg): 55*6777b538SAndroid Build Coastguard Worker sources_bytes.append(read_file_to_string(arg)) 56*6777b538SAndroid Build Coastguard Worker else: 57*6777b538SAndroid Build Coastguard Worker # Otherwise treat it as a web server address. 58*6777b538SAndroid Build Coastguard Worker sources_bytes.append(read_certificates_data_from_server(arg)) 59*6777b538SAndroid Build Coastguard Worker 60*6777b538SAndroid Build Coastguard Worker return sources_bytes 61*6777b538SAndroid Build Coastguard Worker 62*6777b538SAndroid Build Coastguard Worker 63*6777b538SAndroid Build Coastguard Workerdef strip_indentation_whitespace(text): 64*6777b538SAndroid Build Coastguard Worker """Strips leading whitespace from each line.""" 65*6777b538SAndroid Build Coastguard Worker stripped_lines = [line.lstrip() for line in text.split(b"\n")] 66*6777b538SAndroid Build Coastguard Worker return b"\n".join(stripped_lines) 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Worker 69*6777b538SAndroid Build Coastguard Workerdef strip_all_whitespace(text): 70*6777b538SAndroid Build Coastguard Worker pattern = re.compile(rb'\s+') 71*6777b538SAndroid Build Coastguard Worker return re.sub(pattern, b'', text).replace(rb'\n', b'\n') 72*6777b538SAndroid Build Coastguard Worker 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Workerdef extract_certificates_from_pem(pem_bytes): 75*6777b538SAndroid Build Coastguard Worker certificates_der = [] 76*6777b538SAndroid Build Coastguard Worker 77*6777b538SAndroid Build Coastguard Worker regex = re.compile( 78*6777b538SAndroid Build Coastguard Worker rb'-----BEGIN (CERTIFICATE|PKCS7)-----(.*?)(-----END \1-----|$)', 79*6777b538SAndroid Build Coastguard Worker re.DOTALL) 80*6777b538SAndroid Build Coastguard Worker 81*6777b538SAndroid Build Coastguard Worker for match in regex.finditer(pem_bytes): 82*6777b538SAndroid Build Coastguard Worker if not match.group(3): 83*6777b538SAndroid Build Coastguard Worker sys.stderr.write( 84*6777b538SAndroid Build Coastguard Worker "\nUnterminated %s block, input is corrupt or truncated\n" % 85*6777b538SAndroid Build Coastguard Worker match.group(1)) 86*6777b538SAndroid Build Coastguard Worker continue 87*6777b538SAndroid Build Coastguard Worker der = base64.b64decode(strip_all_whitespace(match.group(2))) 88*6777b538SAndroid Build Coastguard Worker if match.group(1) == b'CERTIFICATE': 89*6777b538SAndroid Build Coastguard Worker certificates_der.append(der) 90*6777b538SAndroid Build Coastguard Worker else: 91*6777b538SAndroid Build Coastguard Worker certificates_der.extend(extract_certificates_from_der_pkcs7(der)) 92*6777b538SAndroid Build Coastguard Worker 93*6777b538SAndroid Build Coastguard Worker return certificates_der 94*6777b538SAndroid Build Coastguard Worker 95*6777b538SAndroid Build Coastguard Worker 96*6777b538SAndroid Build Coastguard Workerdef extract_certificates_from_der_pkcs7(der_bytes): 97*6777b538SAndroid Build Coastguard Worker pkcs7_certs_pem = process_data_with_command( 98*6777b538SAndroid Build Coastguard Worker ['openssl','pkcs7','-print_certs', '-inform', 'DER'], der_bytes) 99*6777b538SAndroid Build Coastguard Worker # The output will be one or more PEM encoded certificates. 100*6777b538SAndroid Build Coastguard Worker # (Or CRLS, but those will be ignored.) 101*6777b538SAndroid Build Coastguard Worker if pkcs7_certs_pem: 102*6777b538SAndroid Build Coastguard Worker return extract_certificates_from_pem(pkcs7_certs_pem) 103*6777b538SAndroid Build Coastguard Worker return [] 104*6777b538SAndroid Build Coastguard Worker 105*6777b538SAndroid Build Coastguard Worker 106*6777b538SAndroid Build Coastguard Workerdef extract_certificates_from_der_ascii(input_text): 107*6777b538SAndroid Build Coastguard Worker certificates_der = [] 108*6777b538SAndroid Build Coastguard Worker 109*6777b538SAndroid Build Coastguard Worker # Look for beginning and end of Certificate SEQUENCE. The indentation is 110*6777b538SAndroid Build Coastguard Worker # significant. (The SEQUENCE must be non-indented, and the rest of the DER 111*6777b538SAndroid Build Coastguard Worker # ASCII must be indented until the closing } which again is non-indented.) 112*6777b538SAndroid Build Coastguard Worker # The output of der2ascii meets this, but it is not a requirement of the DER 113*6777b538SAndroid Build Coastguard Worker # ASCII language. 114*6777b538SAndroid Build Coastguard Worker # TODO(mattm): consider alternate approach of doing ascii2der on entire 115*6777b538SAndroid Build Coastguard Worker # input, and handling the multiple concatenated DER certificates. 116*6777b538SAndroid Build Coastguard Worker regex = re.compile(r'^(SEQUENCE {.*?^})', re.DOTALL | re.MULTILINE) 117*6777b538SAndroid Build Coastguard Worker 118*6777b538SAndroid Build Coastguard Worker for match in regex.finditer(input_text): 119*6777b538SAndroid Build Coastguard Worker der_ascii_bytes = match.group(1) 120*6777b538SAndroid Build Coastguard Worker der_bytes = process_data_with_command(["ascii2der"], der_ascii_bytes) 121*6777b538SAndroid Build Coastguard Worker if der_bytes: 122*6777b538SAndroid Build Coastguard Worker certificates_der.append(der_bytes) 123*6777b538SAndroid Build Coastguard Worker 124*6777b538SAndroid Build Coastguard Worker return certificates_der 125*6777b538SAndroid Build Coastguard Worker 126*6777b538SAndroid Build Coastguard Worker 127*6777b538SAndroid Build Coastguard Workerdef decode_netlog_hexdump(netlog_text): 128*6777b538SAndroid Build Coastguard Worker lines = netlog_text.splitlines() 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Worker # Skip the text preceeding the actual hexdump. 131*6777b538SAndroid Build Coastguard Worker while lines and 'bytes =' not in lines[0]: 132*6777b538SAndroid Build Coastguard Worker del lines[0] 133*6777b538SAndroid Build Coastguard Worker if not lines: 134*6777b538SAndroid Build Coastguard Worker return None 135*6777b538SAndroid Build Coastguard Worker del lines[0] 136*6777b538SAndroid Build Coastguard Worker 137*6777b538SAndroid Build Coastguard Worker bytes = [] 138*6777b538SAndroid Build Coastguard Worker hex_re = re.compile('\s*([0-9A-Fa-f ]{48})') 139*6777b538SAndroid Build Coastguard Worker for line in lines: 140*6777b538SAndroid Build Coastguard Worker m = hex_re.search(line) 141*6777b538SAndroid Build Coastguard Worker if not m: 142*6777b538SAndroid Build Coastguard Worker break 143*6777b538SAndroid Build Coastguard Worker hex_string = m.group(1) 144*6777b538SAndroid Build Coastguard Worker bytes.extend(chr(int(part, 16)) for part in hex_string.split()) 145*6777b538SAndroid Build Coastguard Worker 146*6777b538SAndroid Build Coastguard Worker return ''.join(bytes) 147*6777b538SAndroid Build Coastguard Worker 148*6777b538SAndroid Build Coastguard Worker 149*6777b538SAndroid Build Coastguard Workerclass ByteReader: 150*6777b538SAndroid Build Coastguard Worker """Iteratively consume data from a byte string. 151*6777b538SAndroid Build Coastguard Worker 152*6777b538SAndroid Build Coastguard Worker Automatically tracks and advances current position in the string as data is 153*6777b538SAndroid Build Coastguard Worker consumed, and will throw an exception if attempting to read past the end of 154*6777b538SAndroid Build Coastguard Worker the string. 155*6777b538SAndroid Build Coastguard Worker """ 156*6777b538SAndroid Build Coastguard Worker def __init__(self, data): 157*6777b538SAndroid Build Coastguard Worker self.data = data 158*6777b538SAndroid Build Coastguard Worker self.pos = 0 159*6777b538SAndroid Build Coastguard Worker 160*6777b538SAndroid Build Coastguard Worker def consume_byte(self): 161*6777b538SAndroid Build Coastguard Worker i = ord(self.data[self.pos]) 162*6777b538SAndroid Build Coastguard Worker self.pos += 1 163*6777b538SAndroid Build Coastguard Worker return i 164*6777b538SAndroid Build Coastguard Worker 165*6777b538SAndroid Build Coastguard Worker def consume_int16(self): 166*6777b538SAndroid Build Coastguard Worker return ((self.consume_byte() << 8) + self.consume_byte()) 167*6777b538SAndroid Build Coastguard Worker 168*6777b538SAndroid Build Coastguard Worker def consume_int24(self): 169*6777b538SAndroid Build Coastguard Worker return ((self.consume_byte() << 16) + (self.consume_byte() << 8) + 170*6777b538SAndroid Build Coastguard Worker self.consume_byte()) 171*6777b538SAndroid Build Coastguard Worker 172*6777b538SAndroid Build Coastguard Worker def consume_bytes(self, n): 173*6777b538SAndroid Build Coastguard Worker b = self.data[self.pos:self.pos+n] 174*6777b538SAndroid Build Coastguard Worker if len(b) != n: 175*6777b538SAndroid Build Coastguard Worker raise IndexError('requested:%d bytes actual:%d bytes'%(n, len(b))) 176*6777b538SAndroid Build Coastguard Worker self.pos += n 177*6777b538SAndroid Build Coastguard Worker return b 178*6777b538SAndroid Build Coastguard Worker 179*6777b538SAndroid Build Coastguard Worker def remaining_byte_count(self): 180*6777b538SAndroid Build Coastguard Worker return len(self.data) - self.pos 181*6777b538SAndroid Build Coastguard Worker 182*6777b538SAndroid Build Coastguard Worker 183*6777b538SAndroid Build Coastguard Workerdef decode_tls10_certificate_message(reader): 184*6777b538SAndroid Build Coastguard Worker message_length = reader.consume_int24() 185*6777b538SAndroid Build Coastguard Worker if reader.remaining_byte_count() != message_length: 186*6777b538SAndroid Build Coastguard Worker raise RuntimeError( 187*6777b538SAndroid Build Coastguard Worker 'message_length(%d) != remaining_byte_count(%d)\n' % ( 188*6777b538SAndroid Build Coastguard Worker message_length, reader.remaining_byte_count())) 189*6777b538SAndroid Build Coastguard Worker 190*6777b538SAndroid Build Coastguard Worker certificate_list_length = reader.consume_int24() 191*6777b538SAndroid Build Coastguard Worker if reader.remaining_byte_count() != certificate_list_length: 192*6777b538SAndroid Build Coastguard Worker raise RuntimeError( 193*6777b538SAndroid Build Coastguard Worker 'certificate_list_length(%d) != remaining_byte_count(%d)\n' % ( 194*6777b538SAndroid Build Coastguard Worker certificate_list_length, reader.remaining_byte_count())) 195*6777b538SAndroid Build Coastguard Worker 196*6777b538SAndroid Build Coastguard Worker certificates_der = [] 197*6777b538SAndroid Build Coastguard Worker while reader.remaining_byte_count(): 198*6777b538SAndroid Build Coastguard Worker cert_len = reader.consume_int24() 199*6777b538SAndroid Build Coastguard Worker certificates_der.append(reader.consume_bytes(cert_len)) 200*6777b538SAndroid Build Coastguard Worker 201*6777b538SAndroid Build Coastguard Worker return certificates_der 202*6777b538SAndroid Build Coastguard Worker 203*6777b538SAndroid Build Coastguard Worker 204*6777b538SAndroid Build Coastguard Workerdef decode_tls13_certificate_message(reader): 205*6777b538SAndroid Build Coastguard Worker message_length = reader.consume_int24() 206*6777b538SAndroid Build Coastguard Worker if reader.remaining_byte_count() != message_length: 207*6777b538SAndroid Build Coastguard Worker raise RuntimeError( 208*6777b538SAndroid Build Coastguard Worker 'message_length(%d) != remaining_byte_count(%d)\n' % ( 209*6777b538SAndroid Build Coastguard Worker message_length, reader.remaining_byte_count())) 210*6777b538SAndroid Build Coastguard Worker 211*6777b538SAndroid Build Coastguard Worker # Ignore certificate_request_context. 212*6777b538SAndroid Build Coastguard Worker certificate_request_context_length = reader.consume_byte() 213*6777b538SAndroid Build Coastguard Worker reader.consume_bytes(certificate_request_context_length) 214*6777b538SAndroid Build Coastguard Worker 215*6777b538SAndroid Build Coastguard Worker certificate_list_length = reader.consume_int24() 216*6777b538SAndroid Build Coastguard Worker if reader.remaining_byte_count() != certificate_list_length: 217*6777b538SAndroid Build Coastguard Worker raise RuntimeError( 218*6777b538SAndroid Build Coastguard Worker 'certificate_list_length(%d) != remaining_byte_count(%d)\n' % ( 219*6777b538SAndroid Build Coastguard Worker certificate_list_length, reader.remaining_byte_count())) 220*6777b538SAndroid Build Coastguard Worker 221*6777b538SAndroid Build Coastguard Worker certificates_der = [] 222*6777b538SAndroid Build Coastguard Worker while reader.remaining_byte_count(): 223*6777b538SAndroid Build Coastguard Worker # Assume certificate_type is X.509. 224*6777b538SAndroid Build Coastguard Worker cert_len = reader.consume_int24() 225*6777b538SAndroid Build Coastguard Worker certificates_der.append(reader.consume_bytes(cert_len)) 226*6777b538SAndroid Build Coastguard Worker # Ignore extensions. 227*6777b538SAndroid Build Coastguard Worker extension_len = reader.consume_int16() 228*6777b538SAndroid Build Coastguard Worker reader.consume_bytes(extension_len) 229*6777b538SAndroid Build Coastguard Worker 230*6777b538SAndroid Build Coastguard Worker return certificates_der 231*6777b538SAndroid Build Coastguard Worker 232*6777b538SAndroid Build Coastguard Worker 233*6777b538SAndroid Build Coastguard Workerdef decode_tls_certificate_message(certificate_message): 234*6777b538SAndroid Build Coastguard Worker reader = ByteReader(certificate_message) 235*6777b538SAndroid Build Coastguard Worker if reader.consume_byte() != 11: 236*6777b538SAndroid Build Coastguard Worker sys.stderr.write('HandshakeType != 11. Not a Certificate Message.\n') 237*6777b538SAndroid Build Coastguard Worker return [] 238*6777b538SAndroid Build Coastguard Worker 239*6777b538SAndroid Build Coastguard Worker # The TLS certificate message encoding changed in TLS 1.3. Rather than 240*6777b538SAndroid Build Coastguard Worker # require pasting in and parsing the whole handshake to discover the TLS 241*6777b538SAndroid Build Coastguard Worker # version, just try parsing the message with both the old and new encodings. 242*6777b538SAndroid Build Coastguard Worker 243*6777b538SAndroid Build Coastguard Worker # First try the old style certificate message: 244*6777b538SAndroid Build Coastguard Worker try: 245*6777b538SAndroid Build Coastguard Worker return decode_tls10_certificate_message(reader) 246*6777b538SAndroid Build Coastguard Worker except (IndexError, RuntimeError): 247*6777b538SAndroid Build Coastguard Worker tls10_traceback = traceback.format_exc() 248*6777b538SAndroid Build Coastguard Worker 249*6777b538SAndroid Build Coastguard Worker # Restart the ByteReader and consume the HandshakeType byte again. 250*6777b538SAndroid Build Coastguard Worker reader = ByteReader(certificate_message) 251*6777b538SAndroid Build Coastguard Worker reader.consume_byte() 252*6777b538SAndroid Build Coastguard Worker # Try the new style certificate message: 253*6777b538SAndroid Build Coastguard Worker try: 254*6777b538SAndroid Build Coastguard Worker return decode_tls13_certificate_message(reader) 255*6777b538SAndroid Build Coastguard Worker except (IndexError, RuntimeError): 256*6777b538SAndroid Build Coastguard Worker tls13_traceback = traceback.format_exc() 257*6777b538SAndroid Build Coastguard Worker 258*6777b538SAndroid Build Coastguard Worker # Neither attempt succeeded, just dump some error info: 259*6777b538SAndroid Build Coastguard Worker sys.stderr.write("Couldn't parse TLS certificate message\n") 260*6777b538SAndroid Build Coastguard Worker sys.stderr.write("TLS1.0 parse attempt:\n%s\n" % tls10_traceback) 261*6777b538SAndroid Build Coastguard Worker sys.stderr.write("TLS1.3 parse attempt:\n%s\n" % tls13_traceback) 262*6777b538SAndroid Build Coastguard Worker sys.stderr.write("\n") 263*6777b538SAndroid Build Coastguard Worker 264*6777b538SAndroid Build Coastguard Worker return [] 265*6777b538SAndroid Build Coastguard Worker 266*6777b538SAndroid Build Coastguard Worker 267*6777b538SAndroid Build Coastguard Workerdef extract_tls_certificate_message(netlog_text): 268*6777b538SAndroid Build Coastguard Worker raw_certificate_message = decode_netlog_hexdump(netlog_text) 269*6777b538SAndroid Build Coastguard Worker if not raw_certificate_message: 270*6777b538SAndroid Build Coastguard Worker return [] 271*6777b538SAndroid Build Coastguard Worker return decode_tls_certificate_message(raw_certificate_message) 272*6777b538SAndroid Build Coastguard Worker 273*6777b538SAndroid Build Coastguard Worker 274*6777b538SAndroid Build Coastguard Workerdef extract_certificates(source_bytes): 275*6777b538SAndroid Build Coastguard Worker if b"BEGIN CERTIFICATE" in source_bytes or b"BEGIN PKCS7" in source_bytes: 276*6777b538SAndroid Build Coastguard Worker return extract_certificates_from_pem(source_bytes) 277*6777b538SAndroid Build Coastguard Worker 278*6777b538SAndroid Build Coastguard Worker if b"SEQUENCE {" in source_bytes: 279*6777b538SAndroid Build Coastguard Worker return extract_certificates_from_der_ascii(source_bytes) 280*6777b538SAndroid Build Coastguard Worker 281*6777b538SAndroid Build Coastguard Worker if b"SSL_HANDSHAKE_MESSAGE_RECEIVED" in source_bytes: 282*6777b538SAndroid Build Coastguard Worker return extract_tls_certificate_message(source_bytes) 283*6777b538SAndroid Build Coastguard Worker 284*6777b538SAndroid Build Coastguard Worker # DER encoding of PKCS #7 signedData OID (1.2.840.113549.1.7.2) 285*6777b538SAndroid Build Coastguard Worker if b"\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x07\x02" in source_bytes: 286*6777b538SAndroid Build Coastguard Worker return extract_certificates_from_der_pkcs7(source_bytes) 287*6777b538SAndroid Build Coastguard Worker 288*6777b538SAndroid Build Coastguard Worker # Otherwise assume it is the DER for a single certificate 289*6777b538SAndroid Build Coastguard Worker return [source_bytes] 290*6777b538SAndroid Build Coastguard Worker 291*6777b538SAndroid Build Coastguard Worker 292*6777b538SAndroid Build Coastguard Workerdef process_data_with_command(command, data): 293*6777b538SAndroid Build Coastguard Worker try: 294*6777b538SAndroid Build Coastguard Worker p = subprocess.Popen(command, 295*6777b538SAndroid Build Coastguard Worker stdin=subprocess.PIPE, 296*6777b538SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 297*6777b538SAndroid Build Coastguard Worker stderr=subprocess.PIPE) 298*6777b538SAndroid Build Coastguard Worker except OSError as e: 299*6777b538SAndroid Build Coastguard Worker if e.errno == errno.ENOENT: 300*6777b538SAndroid Build Coastguard Worker sys.stderr.write("Failed to execute %s\n" % command[0]) 301*6777b538SAndroid Build Coastguard Worker return b"" 302*6777b538SAndroid Build Coastguard Worker raise 303*6777b538SAndroid Build Coastguard Worker 304*6777b538SAndroid Build Coastguard Worker result = p.communicate(data) 305*6777b538SAndroid Build Coastguard Worker 306*6777b538SAndroid Build Coastguard Worker if p.returncode == 0: 307*6777b538SAndroid Build Coastguard Worker return result[0] 308*6777b538SAndroid Build Coastguard Worker 309*6777b538SAndroid Build Coastguard Worker # Otherwise failed. 310*6777b538SAndroid Build Coastguard Worker sys.stderr.write("Failed: %s: %s\n" % (" ".join(command), result[1])) 311*6777b538SAndroid Build Coastguard Worker return b"" 312*6777b538SAndroid Build Coastguard Worker 313*6777b538SAndroid Build Coastguard Worker 314*6777b538SAndroid Build Coastguard Workerdef openssl_text_pretty_printer(certificate_der, unused_certificate_number): 315*6777b538SAndroid Build Coastguard Worker return process_data_with_command(["openssl", "x509", "-text", "-inform", 316*6777b538SAndroid Build Coastguard Worker "DER", "-noout"], certificate_der) 317*6777b538SAndroid Build Coastguard Worker 318*6777b538SAndroid Build Coastguard Worker 319*6777b538SAndroid Build Coastguard Workerdef pem_pretty_printer(certificate_der, unused_certificate_number): 320*6777b538SAndroid Build Coastguard Worker return process_data_with_command(["openssl", "x509", "-inform", "DER", 321*6777b538SAndroid Build Coastguard Worker "-outform", "PEM"], certificate_der) 322*6777b538SAndroid Build Coastguard Worker 323*6777b538SAndroid Build Coastguard Worker 324*6777b538SAndroid Build Coastguard Workerdef der2ascii_pretty_printer(certificate_der, unused_certificate_number): 325*6777b538SAndroid Build Coastguard Worker return process_data_with_command(["der2ascii"], certificate_der) 326*6777b538SAndroid Build Coastguard Worker 327*6777b538SAndroid Build Coastguard Worker 328*6777b538SAndroid Build Coastguard Workerdef header_pretty_printer(certificate_der, certificate_number): 329*6777b538SAndroid Build Coastguard Worker cert_hash = hashlib.sha256(certificate_der).hexdigest() 330*6777b538SAndroid Build Coastguard Worker s = """=========================================== 331*6777b538SAndroid Build Coastguard WorkerCertificate%d: %s 332*6777b538SAndroid Build Coastguard Worker===========================================""" % (certificate_number, cert_hash) 333*6777b538SAndroid Build Coastguard Worker return s.encode("ascii") 334*6777b538SAndroid Build Coastguard Worker 335*6777b538SAndroid Build Coastguard Worker 336*6777b538SAndroid Build Coastguard Worker# This is actually just used as a magic value, since pretty_print_certificates 337*6777b538SAndroid Build Coastguard Worker# special-cases der output. 338*6777b538SAndroid Build Coastguard Workerdef der_printer(): 339*6777b538SAndroid Build Coastguard Worker raise RuntimeError 340*6777b538SAndroid Build Coastguard Worker 341*6777b538SAndroid Build Coastguard Worker 342*6777b538SAndroid Build Coastguard Workerdef pretty_print_certificates(certificates_der, pretty_printers): 343*6777b538SAndroid Build Coastguard Worker # Need to special-case DER output to avoid adding any newlines, and to 344*6777b538SAndroid Build Coastguard Worker # only allow a single certificate to be output. 345*6777b538SAndroid Build Coastguard Worker if pretty_printers == [der_printer]: 346*6777b538SAndroid Build Coastguard Worker if len(certificates_der) > 1: 347*6777b538SAndroid Build Coastguard Worker sys.stderr.write("DER output only supports a single certificate, " 348*6777b538SAndroid Build Coastguard Worker "ignoring %d remaining certs\n" % ( 349*6777b538SAndroid Build Coastguard Worker len(certificates_der) - 1)) 350*6777b538SAndroid Build Coastguard Worker return certificates_der[0] 351*6777b538SAndroid Build Coastguard Worker 352*6777b538SAndroid Build Coastguard Worker result = b"" 353*6777b538SAndroid Build Coastguard Worker for i in range(len(certificates_der)): 354*6777b538SAndroid Build Coastguard Worker certificate_der = certificates_der[i] 355*6777b538SAndroid Build Coastguard Worker pretty = [] 356*6777b538SAndroid Build Coastguard Worker for pretty_printer in pretty_printers: 357*6777b538SAndroid Build Coastguard Worker pretty_printed = pretty_printer(certificate_der, i) 358*6777b538SAndroid Build Coastguard Worker if pretty_printed: 359*6777b538SAndroid Build Coastguard Worker pretty.append(pretty_printed) 360*6777b538SAndroid Build Coastguard Worker result += b"\n".join(pretty) + b"\n" 361*6777b538SAndroid Build Coastguard Worker return result 362*6777b538SAndroid Build Coastguard Worker 363*6777b538SAndroid Build Coastguard Worker 364*6777b538SAndroid Build Coastguard Workerdef parse_outputs(outputs): 365*6777b538SAndroid Build Coastguard Worker pretty_printers = [] 366*6777b538SAndroid Build Coastguard Worker output_map = {"der2ascii": der2ascii_pretty_printer, 367*6777b538SAndroid Build Coastguard Worker "openssl_text": openssl_text_pretty_printer, 368*6777b538SAndroid Build Coastguard Worker "pem": pem_pretty_printer, 369*6777b538SAndroid Build Coastguard Worker "header": header_pretty_printer, 370*6777b538SAndroid Build Coastguard Worker "der": der_printer} 371*6777b538SAndroid Build Coastguard Worker for output_name in outputs.split(','): 372*6777b538SAndroid Build Coastguard Worker if output_name not in output_map: 373*6777b538SAndroid Build Coastguard Worker sys.stderr.write("Invalid output type: %s\n" % output_name) 374*6777b538SAndroid Build Coastguard Worker return [] 375*6777b538SAndroid Build Coastguard Worker pretty_printers.append(output_map[output_name]) 376*6777b538SAndroid Build Coastguard Worker if der_printer in pretty_printers and len(pretty_printers) > 1: 377*6777b538SAndroid Build Coastguard Worker sys.stderr.write("Output type der must be used alone.\n") 378*6777b538SAndroid Build Coastguard Worker return [] 379*6777b538SAndroid Build Coastguard Worker return pretty_printers 380*6777b538SAndroid Build Coastguard Worker 381*6777b538SAndroid Build Coastguard Worker 382*6777b538SAndroid Build Coastguard Workerdef main(): 383*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 384*6777b538SAndroid Build Coastguard Worker description=__doc__, formatter_class=argparse.RawTextHelpFormatter) 385*6777b538SAndroid Build Coastguard Worker 386*6777b538SAndroid Build Coastguard Worker parser.add_argument('sources', metavar='SOURCE', nargs='*', 387*6777b538SAndroid Build Coastguard Worker help='''Each SOURCE can be one of: 388*6777b538SAndroid Build Coastguard Worker (1) A server name such as www.google.com. 389*6777b538SAndroid Build Coastguard Worker (2) A PEM [*] file containing one or more CERTIFICATE or PKCS7 blocks 390*6777b538SAndroid Build Coastguard Worker (3) A file containing one or more DER ASCII certificates 391*6777b538SAndroid Build Coastguard Worker (4) A text NetLog dump of a TLS certificate message 392*6777b538SAndroid Build Coastguard Worker (must include the SSL_HANDSHAKE_MESSAGE_RECEIVED line) 393*6777b538SAndroid Build Coastguard Worker (5) A binary file containing DER-encoded PKCS #7 signedData 394*6777b538SAndroid Build Coastguard Worker (6) A binary file containing DER-encoded certificate 395*6777b538SAndroid Build Coastguard Worker 396*6777b538SAndroid Build Coastguard WorkerWhen multiple SOURCEs are listed, all certificates in them 397*6777b538SAndroid Build Coastguard Workerare concatenated. If no SOURCE is given then data will be 398*6777b538SAndroid Build Coastguard Workerread from stdin. 399*6777b538SAndroid Build Coastguard Worker 400*6777b538SAndroid Build Coastguard Worker[*] Parsing of PEM files is relaxed - leading indentation 401*6777b538SAndroid Build Coastguard Workerwhitespace will be stripped (needed for copy-pasting data 402*6777b538SAndroid Build Coastguard Workerfrom NetLogs).''') 403*6777b538SAndroid Build Coastguard Worker 404*6777b538SAndroid Build Coastguard Worker parser.add_argument('--output', 405*6777b538SAndroid Build Coastguard Worker dest='outputs', 406*6777b538SAndroid Build Coastguard Worker action='store', 407*6777b538SAndroid Build Coastguard Worker default="header,openssl_text,pem", 408*6777b538SAndroid Build Coastguard Worker help='output formats to use. Default: %(default)s') 409*6777b538SAndroid Build Coastguard Worker 410*6777b538SAndroid Build Coastguard Worker args = parser.parse_args() 411*6777b538SAndroid Build Coastguard Worker 412*6777b538SAndroid Build Coastguard Worker sources_bytes = read_sources_from_commandline(args.sources) 413*6777b538SAndroid Build Coastguard Worker 414*6777b538SAndroid Build Coastguard Worker pretty_printers = parse_outputs(args.outputs) 415*6777b538SAndroid Build Coastguard Worker if not pretty_printers: 416*6777b538SAndroid Build Coastguard Worker sys.stderr.write('No pretty printers selected.\n') 417*6777b538SAndroid Build Coastguard Worker sys.exit(1) 418*6777b538SAndroid Build Coastguard Worker 419*6777b538SAndroid Build Coastguard Worker certificates_der = [] 420*6777b538SAndroid Build Coastguard Worker for source_bytes in sources_bytes: 421*6777b538SAndroid Build Coastguard Worker certificates_der.extend(extract_certificates(source_bytes)) 422*6777b538SAndroid Build Coastguard Worker 423*6777b538SAndroid Build Coastguard Worker sys.stdout.buffer.write( 424*6777b538SAndroid Build Coastguard Worker pretty_print_certificates(certificates_der, pretty_printers)) 425*6777b538SAndroid Build Coastguard Worker 426*6777b538SAndroid Build Coastguard Worker 427*6777b538SAndroid Build Coastguard Workerif __name__ == "__main__": 428*6777b538SAndroid Build Coastguard Worker main() 429