Check SSL certificate renewal deadline using Python3 M2Crypto library

background

In the domain where the certificate is automatically renewed by Let's Encrypt, if you follow the manual, the SSL certificate should be renewed at the timing of less than 30 days with the default setting, but the renewal is carried out. I implemented it with a script because I needed to check if it was. Actually, the following is operated by feeding the config file of the domain list to be checked, but since there was not much explanation of the M2Crypto library used for implementation, I will transcribe it including the record of the actual operation. .. I wish I could implement it with urllib, which I'm used to, but I didn't know how to verify the SSL certificate, so I used the M2Crypto library.

environment

Library to use

Python runtime environment

A python3 environment has been created using Pyenv like this.

 pwd
/root/python3

 pyenv versions
  system
* 3.5.6 (set by /root/python3/.python-version)

 python -V
Python 3.5.6

SSL certificate expiration confirmation

We will prepare what you need while checking the data status etc. using the interactive interactive mode.

import ssl
import M2Crypto
import datetime

port = 443
 hostname ='www.qiita.com' # Domain is your own.

cert = ssl.get_server_certificate((hostname, port))

x509 = M2Crypto.X509.load_cert_string(cert)

x509.get_subject().as_text()    # 'CN=qiita.com'

From this area, I don't know much because I can't find much documentation, so I will proceed while digging out usable methods.

dir(x509)
 ['__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_ptr', '_pyfree', 'add_ext', 'as_der', 'as_pem',
 'as_text', 'check_ca', 'check_purpose', 'get_ext', 'get_ext_at', 'get_ext_count', 'get_fingerprint', 'get_issuer', 'get_not_after',
 'get_not_before', 'get_pubkey', 'get_serial_number', 'get_subject', 'get_version', 'm2_x509_free', 'save', 'save_pem', 'set_issuer',
 'set_issuer_name', 'set_not_after', 'set_not_before', 'set_pubkey', 'set_serial_number', 'set_subject', 'set_subject_name',
 'set_version', 'sign', 'verify', 'x509']

I found get_not_after that seems to be usable.

type(x509.get_not_after())         # <M2Crypto.ASN1.ASN1_TIME object at 0x7f26999f1940>

dir(x509.get_not_after())
 ['__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_ptr', '_pyfree', '_ssl_months', 'asn1_time',
 'get_datetime', 'm2_asn1_time_free', 'set_datetime', 'set_string', 'set_time']

I found something called get_datetime that could be output with datetime.

type(x509.get_not_after().get_datetime())  # <class 'datetime.datetime'>

x509.get_not_after().get_datetime()        # datetime.datetime(2020, 4, 30, 12, 0, tzinfo=<Timezone: UTC>)

Since the data was acquired by datetime, it is OK if you use the datetime library to find the timedelta.

exp_date = x509.get_not_after().get_datetime()

now = datetime.datetime.now()            # datetime.datetime(2019, 11, 29, 10, 52, 24, 89337)

exp_date - now
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't subtract offset-naive and offset-aware datetimes

It seems that subtraction between datetime is not possible if the time zones are attached. Reference: Handling Python time zone So, I cut down the time zone information and compared.

remaining_time = exp_date.replace(tzinfo=None) - now       # datetime.timedelta(53, 15629, 910663)

remaining_time.days
152

I was able to successfully obtain the remaining days of the SSL certificate.

The final code is below. It is relatively compact.

import ssl
import M2Crypto
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import datetime

port = 443
 hostname ='www.qiita.com' # Please rewrite as appropriate or read the arguments

cert = ssl.get_server_certificate((hostname, port))
x509 = M2Crypto.X509.load_cert_string(cert)
exp_date = x509.get_not_after().get_datetime()
now = datetime.datetime.now()
remaining_time = exp_date.replace(tzinfo=None) - now

print(remaining_time.days)
152

Reference information

Recommended Posts

Check SSL certificate renewal deadline using Python3 M2Crypto library
Check python code styles using pep8
Install python library on Lambda using [/ tmp]
Check stock prices with slackbot using python
[Hyperledger Iroha] Create an account using Python library
[Personal memo] julia --Using Python library with julia using PyCall
Aggregate test results using the QualityForward Python library
Chord recognition using chromagram of python library librosa
Introduction of Python Imaging Library (PIL) using HomeBrew