1#!/usr/bin/env python3 2# 3# fetch the certificate that the server(s) are providing in PEM form 4# 5# args are HOST:PORT [, HOST:PORT...] 6# 7# By Bill Janssen. 8 9import re 10import os 11import sys 12import tempfile 13 14 15def fetch_server_certificate (host, port): 16 17 def subproc(cmd): 18 from subprocess import Popen, PIPE, STDOUT 19 proc = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) 20 status = proc.wait() 21 output = proc.stdout.read() 22 return status, output 23 24 def strip_to_x509_cert(certfile_contents, outfile=None): 25 m = re.search(br"^([-]+BEGIN CERTIFICATE[-]+[\r]*\n" 26 br".*[\r]*^[-]+END CERTIFICATE[-]+)$", 27 certfile_contents, re.MULTILINE | re.DOTALL) 28 if not m: 29 return None 30 else: 31 tn = tempfile.mktemp() 32 fp = open(tn, "wb") 33 fp.write(m.group(1) + b"\n") 34 fp.close() 35 try: 36 tn2 = (outfile or tempfile.mktemp()) 37 status, output = subproc(r'openssl x509 -in "%s" -out "%s"' % 38 (tn, tn2)) 39 if status != 0: 40 raise RuntimeError('OpenSSL x509 failed with status %s and ' 41 'output: %r' % (status, output)) 42 fp = open(tn2, 'rb') 43 data = fp.read() 44 fp.close() 45 os.unlink(tn2) 46 return data 47 finally: 48 os.unlink(tn) 49 50 if sys.platform.startswith("win"): 51 tfile = tempfile.mktemp() 52 fp = open(tfile, "w") 53 fp.write("quit\n") 54 fp.close() 55 try: 56 status, output = subproc( 57 'openssl s_client -connect "%s:%s" -showcerts < "%s"' % 58 (host, port, tfile)) 59 finally: 60 os.unlink(tfile) 61 else: 62 status, output = subproc( 63 'openssl s_client -connect "%s:%s" -showcerts < /dev/null' % 64 (host, port)) 65 if status != 0: 66 raise RuntimeError('OpenSSL connect failed with status %s and ' 67 'output: %r' % (status, output)) 68 certtext = strip_to_x509_cert(output) 69 if not certtext: 70 raise ValueError("Invalid response received from server at %s:%s" % 71 (host, port)) 72 return certtext 73 74 75if __name__ == "__main__": 76 if len(sys.argv) < 2: 77 sys.stderr.write( 78 "Usage: %s HOSTNAME:PORTNUMBER [, HOSTNAME:PORTNUMBER...]\n" % 79 sys.argv[0]) 80 sys.exit(1) 81 for arg in sys.argv[1:]: 82 host, port = arg.split(":") 83 sys.stdout.buffer.write(fetch_server_certificate(host, int(port))) 84 sys.exit(0) 85