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.
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
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
Recommended Posts