LoginSignup
0
1

More than 3 years have passed since last update.

Python3 M2Cryptoライブラリを使用してSSL証明書の更新期限チェックをする

Last updated at Posted at 2019-11-29

背景

Let's Encryptで証明書の自動更新をしているドメインで、マニュアルどおりにやっていれば、デフォルト設定で30日を切ったタイミングでSSL証明書の更新が行われるはずですが、更新が実施されているかチェックする必要性があったためスクリプトで実装しました。
実際には下記をチェック対象ドメインリストのコンフィグファイルを食わせるなどして運用しますが、実装に利用したM2Cryptoライブラリの説明があまりなかったので、実際に操作した記録を含めて書き起こしておきます。
本当は使い慣れてるurllibとかで実装できればよかったんですが、SSL証明書の検証ができる方法がよくわからなかったのでM2Cryptoライブラリを利用しました。

環境

  • Amazon Linux
  • Python3

利用するライブラリ

  • datetime
  • ssl
  • M2Crypto

Python実行環境

こんな感じでPyenvを利用してpython3環境を作成済み。

# pwd
/root/python3

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

# python -V
Python 3.5.6

SSL証明書の期限確認

対話型のインタラクティブモードを利用してデータ状態などを確認しながら必要なものを揃えていきます。

import ssl
import M2Crypto
import datetime

port = 443
hostname = 'www.qiita.com'      # ドメインは適当にご自身のものを。

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

x509 = M2Crypto.X509.load_cert_string(cert)

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

この辺から、あんまりドキュメントが見当たらなくてよくわからないので、使えるメソッドを掘り出しながら進めます。

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']

使えそうなget_not_afterを発見。

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']

datetimeで出力できそうな get_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>)

datetimeでデータが取得できたので、後はdatetimeライブラリを利用してtimedeltaを求めてあげればOK

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

datetime同士の引き算はタイムゾーンがくっついてるとできないそうです。
参考 : Pythonのタイムゾーンの扱い
ということで、タイムゾーン情報を削って比較。

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

remaining_time.days
152

無事SSL証明書の残り日数の取得ができました。

最終的なコードは以下。
割とコンパクト。

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

port = 443
hostname = 'www.qiita.com'     # 適宜書き換えるか引数を読み込んで下さい

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

参考情報

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1