Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Python script to check on SSL certificates
# -*- encoding: utf-8 -*-
# requires a recent enough python with idna support in socket
# pyopenssl, cryptography and idna
from OpenSSL import SSL
from cryptography import x509
from cryptography.x509.oid import NameOID
import idna
from socket import socket
from collections import namedtuple
HostInfo = namedtuple(field_names='cert hostname peername', typename='HostInfo')
HOSTS = [
('damjan.softver.org.mk', 443),
('expired.badssl.com', 443),
('wrong.host.badssl.com', 443),
('ca.ocsr.nl', 443),
('faß.de', 443),
('самодеј.мкд', 443),
]
def verify_cert(cert, hostname):
# verify notAfter/notBefore, CA trusted, servername/sni/hostname
cert.has_expired()
# service_identity.pyopenssl.verify_hostname(client_ssl, hostname)
# issuer
def get_certificate(hostname, port):
hostname_idna = idna.encode(hostname)
sock = socket()
sock.connect((hostname, port))
peername = sock.getpeername()
ctx = SSL.Context(SSL.SSLv23_METHOD) # most compatible
ctx.check_hostname = False
ctx.verify_mode = SSL.VERIFY_NONE
sock_ssl = SSL.Connection(ctx, sock)
sock_ssl.set_connect_state()
sock_ssl.set_tlsext_host_name(hostname_idna)
sock_ssl.do_handshake()
cert = sock_ssl.get_peer_certificate()
crypto_cert = cert.to_cryptography()
sock_ssl.close()
sock.close()
return HostInfo(cert=crypto_cert, peername=peername, hostname=hostname)
def get_alt_names(cert):
try:
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
return ext.value.get_values_for_type(x509.DNSName)
except x509.ExtensionNotFound:
return None
def get_common_name(cert):
try:
names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
return names[0].value
except x509.ExtensionNotFound:
return None
def get_issuer(cert):
try:
names = cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)
return names[0].value
except x509.ExtensionNotFound:
return None
def print_basic_info(hostinfo):
s = '''» {hostname} « … {peername}
\tcommonName: {commonname}
\tSAN: {SAN}
\tissuer: {issuer}
\tnotBefore: {notbefore}
\tnotAfter: {notafter}
'''.format(
hostname=hostinfo.hostname,
peername=hostinfo.peername,
commonname=get_common_name(hostinfo.cert),
SAN=get_alt_names(hostinfo.cert),
issuer=get_issuer(hostinfo.cert),
notbefore=hostinfo.cert.not_valid_before,
notafter=hostinfo.cert.not_valid_after
)
print(s)
def check_it_out(hostname, port):
hostinfo = get_certificate(hostname, port)
print_basic_info(hostinfo)
import concurrent.futures
if __name__ == '__main__':
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
for hostinfo in e.map(lambda x: get_certificate(x[0], x[1]), HOSTS):
print_basic_info(hostinfo)
@inquisitivefrog

This comment has been minimized.

Copy link

inquisitivefrog commented Mar 21, 2019

This helped me out of a jam. thank you.

@kevyteky

This comment has been minimized.

Copy link

kevyteky commented Jul 19, 2019

Appreciate all your efforts! Thanks!

@redmine-cosi

This comment has been minimized.

Copy link

redmine-cosi commented Aug 18, 2019

Hi,
the script is useful, but it's possible put these values:
('damjan.softver.org.mk', 443),
('expired.badssl.com', 443),
('wrong.host.badssl.com', 443),
('ca.ocsr.nl', 443),
('faß.de', 443),
('самодеј.мкд', 443),
inside a text file? I mean exist an elegant way to read lines of file into list?

@gdamjan

This comment has been minimized.

Copy link
Owner Author

gdamjan commented Aug 18, 2019

@redmine-cosi:

sure, why not. open a file, read line by line, and split on : or empty space, or anything. maybe use csv module?

@redmine-cosi

This comment has been minimized.

Copy link

redmine-cosi commented Aug 18, 2019

Uhm, Things are really complicated for me right now :( I tried so:
...
with open("cert-list.csv") as filecsv:
HOSTS = csv.reader(filecsv, delimiter=',')
for row, line in enumerate(HOSTS):
a = '{}'.format(line)
print(a)
print (type(line))
print (type(row))
...
my .csv file:
pd.trust.it, 443
dev.scinetwork.it, 443
cs.fibc.it, 443
ouput:
['pd.trust.it', ' 443']
<class 'list'>
<class 'int'>
['dev.scinetwork.it', ' 443']
<class 'list'>
<class 'int'>
['cs.fibc.it', ' 443']
<class 'list'>
<class 'int'>
Traceback (most recent call last):
File "cert-ssl-check.py", line 100, in
for hostinfo in e.map(lambda x: get_certificate(x[0], x[1]), HOSTS):
File "/usr/lib/python3.5/concurrent/futures/_base.py", line 548, in map
fs = [self.submit(fn, *args) for args in zip(*iterables)]
File "/usr/lib/python3.5/concurrent/futures/_base.py", line 548, in
fs = [self.submit(fn, *args) for args in zip(*iterables)]
ValueError: I/O operation on closed file.

@gdamjan

This comment has been minimized.

Copy link
Owner Author

gdamjan commented Aug 19, 2019

I don't think gist comments are the best place to learn python

@wiperpaul

This comment has been minimized.

Copy link

wiperpaul commented Sep 4, 2019

Are you able to distinguish the certificate type from this info e.g. ('OV SSL', 'EV SSL', 'DV SSL') ?

@gdamjan

This comment has been minimized.

Copy link
Owner Author

gdamjan commented Sep 4, 2019

Are you able to distinguish the certificate type from this info e.g. ('OV SSL', 'EV SSL', 'DV SSL') ?

afaik yes, if you give me an example site with those attributes I can take a look how to extract those from the cert info

@wiperpaul

This comment has been minimized.

Copy link

wiperpaul commented Sep 5, 2019

For example PayPal.com has an expensive EV(Extended Validation) SSL cert but the only indication I've been able to find is occasionally sites have 'Extended Validation Server' in their CN field.

This isn't standard either, some just have a name like 'Google Trust Services' like for Google.com.

Edit - I just found this post clearing some of this up for me, https://security.stackexchange.com/questions/88721/how-to-detect-whether-a-ca-used-ev-for-a-certificate-using-openssl

@gdamjan

This comment has been minimized.

Copy link
Owner Author

gdamjan commented Sep 5, 2019

https://en.wikipedia.org/wiki/Extended_Validation_Certificate

EV certificates are different from domain-validated certificates and organization-validation certificates in that they can be issued only by a subset of certificate authorities (CAs) and require verification of the requesting entity's legal identity before certificate issuance.

so you'll need to have a list of the CAs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.