I created a simple Python program to get the expiry date of SSL cert, from reference on the Internet. It works correctly for cert that is still not expired. But for cert that already expired, an error was raised during the socket handshake due to the cert expiry.
How do I get the expired cert info to extract the expiry date because the connection is refused. Is there a way to force the socket connection to establish even though the cert might be expired?
Code:
import ssl
from cryptography import x509
import sys
import socket
hostname = sys.argv[1]
context = ssl.create_default_context()
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print("SSL/TLS version:",ssock.version())
print()
data = ssock.getpeercert()
print("Data:",data)
print()
notafter_date = data["notAfter"]
print("Expiry date:",notafter_date)
print()
Output for not expired cert:
$ python check_ssl_cert.py badssl.com
SSL/TLS version: TLSv1.2
Data: {'subject': ((('countryName', 'US'),), (('stateOrProvinceName', 'California'),), (('localityName', 'Walnut Creek'),), (('organizationName', 'Lucas Garron Torres'),), (('commonName', '*.badssl.com'),)), 'issuer': ((('countryName', 'US'),), (('organizationName', 'DigiCert Inc'),), (('commonName', 'DigiCert SHA2 Secure Server CA'),)), 'version': 3, 'serialNumber': '0AF06CDA37A60B641342F0A1EB1D59FD', 'notBefore': 'Mar 23 00:00:00 2020 GMT', 'notAfter': 'May 17 12:00:00 2022 GMT', 'subjectAltName': (('DNS', '*.badssl.com'), ('DNS', 'badssl.com')), 'OCSP': ('http://ocsp.digicert.com',), 'caIssuers': ('http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt',), 'crlDistributionPoints': ('http://crl3.digicert.com/ssca-sha2-g6.crl', 'http://crl4.digicert.com/ssca-sha2-g6.crl')}
Expiry date: May 17 12:00:00 2022 GMT
Output for expired cert:
$ python check_ssl_cert.py expired.badssl.com
Traceback (most recent call last):
File "check_ssl_cert.py", line 11, in <module>
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
File "/usr/lib/python3.7/ssl.py", line 423, in wrap_socket
session=session
File "/usr/lib/python3.7/ssl.py", line 870, in _create
self.do_handshake()
File "/usr/lib/python3.7/ssl.py", line 1139, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1091)
As suggested for the other solution, it does not solve the issue.
I tried changing the line
data = ssock.getpeercert()
to
data = ssock.getpeercert(True)
and a DER formatted cert is returned for not expired cert, but got cert verification error for already expired cert.
I managed to create a working solution. Check my Github gist here: https://gist.github.com/sharuzzaman/8827ef0d9fff89e4e937579b2b01653f
Also the verbatim code here for quick reference
#!/bin/env python3
# check_ssl_cert.py - python get info for expired SSL cert
# Copyright 2022 Sharuzzaman Ahmat Raslan <sharuzzaman@gmail.com>
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License along with this program. If not, see https://www.gnu.org/licenses/.
from cryptography import x509
import socket
import ssl
import sys
hostname = sys.argv[1]
# create default context
context = ssl.create_default_context()
# override context so that it can get expired cert
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print("SSL/TLS version:", ssock.version())
print()
# get cert in DER format
data = ssock.getpeercert(True)
print("Data:", data)
print()
# convert cert to PEM format
pem_data = ssl.DER_cert_to_PEM_cert(data)
print("PEM cert:", pem_data)
# pem_data in string. convert to bytes using str.encode()
# extract cert info from PEM format
cert_data = x509.load_pem_x509_certificate(str.encode(pem_data))
# show cert expiry date
print("Expiry date:", cert_data.not_valid_after)
print()