Reputation: 31
I'm working on a Python 2.7.13 (Win x64) script to verify SSL certificates, and alert for problems. However, I'm running into an issue where the script will return information only if the certificate is valid.
If the certificate is invalid, I receive a CERTIFICATE_VERIFY_FAILED SSL error. Normally I would simply use a try/catch when the error is raised and just alert that the cert is invalid, but the issue here is that the I need the actual date the certificate expired.
Per https://docs.python.org/2/library/ssl.html I tried to use conn._https_verify_certificates(enable=False) to disable certificate validation, but get an error that the attribute _https_verify_certificates doesn't exist.
Here is my code so far. I'm sure I'm missing something obvious. Surely Python can pull the SSL certificate without validating it, right?
import socket
import ssl
def ssl_expiry_datetime(hostname):
ssl_date_fmt = r'%b %d %H:%M:%S %Y %Z'
context = ssl.create_default_context()
conn = context.wrap_socket(
socket.socket(socket.AF_INET),
server_hostname=hostname,
)
# 3 second timeout because Lambda has runtime limitations
conn.settimeout(3.0)
#conn._https_verify_certificates(enable=False)
conn.connect((hostname, 443))
ssl_info = conn.getpeercert()
# parse the string from the certificate into a Python datetime object
return ['notAfter']
myhost = 'www.google.com'
print ssl_expiry_datetime(myhost)
Many thanks!!!!
Upvotes: 3
Views: 1544
Reputation: 111
After a lot of trial and error I have found that you can switch off the SSLcertificate hostname verification by using the check_hostname feature.
context.check_hostname = False
This will allow your program to connect on the web server if the target does not match with the Common Name (CN) on the SSL certificate. However, when the web server uses an invalid SSL certificate the connection will fail and throw a ConnetionError error. If the aim is to fetch all SSL certificate even the invalidate ones the following solution will only partially accommodate your needs.
Here is a proposed solution:
import socket, ssl
def ssl_expiry_datetime(hostname):
ssl_date_fmt = r'%b %d %H:%M:%S %Y %Z'
context = ssl.create_default_context()
context.check_hostname = False
conn = context.wrap_socket(
socket.socket(socket.AF_INET),
server_hostname=hostname,
)
# 3 second timeout because Lambda has runtime limitations
conn.settimeout(3.0)
#conn._https_verify_certificates(enable=False)
conn.connect((hostname, 443))
ssl_info = conn.getpeercert()
# parse the string from the certificate into a Python datetime object
return ['notAfter']
myhost = 'example.com'
print ssl_expiry_datetime(myhost)
Alternatively you can use the requests library which allows you to switch off verification completely.
References:
Upvotes: 1