pyOpenSSL で SSL/TLS サーバ証明書の有効期限を取得する
以前に 証明書の有効期限を確認する Python スクリプト というメモを書きました。 このメモでは pyOpenSSL を利用していました。 他にも Python から SSL/TLS サーバ証明書を取り扱いたい場合、以下のようなライブラリを利用出来るようです。
pyOpenSSL 以外はあまりメンテナンスされていないようです。 以前のメモでは機能をメソッドとして実装していたのですが、クラス/プロパティとして実装した方が扱いやすかったので改造したサンプルスクリプトをメモしておきます。
検証環境
以下の環境で検証しました。
- macOS Sonoma 14.2.1
 
- M1
 
- Python 3.12.1
 
pyOpenSSL のインストール
pip でインストールします。
 | python3 -m pip install pyopenssl
  | 
 
クラス/プロパティ版のサンプルスクリプト
証明書の有効期間から開始・終了日時を取得するサンプルは以下の通りです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81  | #!/usr/bin/env python3
import datetime
import ssl
import time
import OpenSSL
class Certificate:
    def __init__(self, hostname: str, port: int):
        self.cert = ssl.get_server_certificate((hostname, port))
        self.x509 = OpenSSL.crypto.load_certificate(
            OpenSSL.crypto.FILETYPE_PEM, self.cert
        )
        __offset__ = time.timezone if (time.localtime().tm_isdst == 0) else time.altzone
        self.offset = __offset__ / 60 / 60 * -1
    @property
    def expired(self) -> bool:
        return self.x509.has_expired()
    @property
    def issuer(self) -> str:
        return self.x509.get_issuer().commonName
    @property
    def not_before(self) -> datetime.datetime:
        return datetime.datetime.strptime(
            str(self.x509.get_notBefore())[2:16], "%Y%m%d%H%M%S"
        ) + datetime.timedelta(hours=self.offset)
    @property
    def not_after(self) -> datetime.datetime:
        return datetime.datetime.strptime(
            str(self.x509.get_notAfter())[2:16], "%Y%m%d%H%M%S"
        ) + datetime.timedelta(hours=self.offset)
    @property
    def pubkey(self) -> str:
        return OpenSSL.crypto.dump_publickey(
            OpenSSL.crypto.FILETYPE_PEM, self.x509.get_pubkey()
        ).decode()
    @property
    def serial_number(self) -> int:
        return self.x509.get_serial_number()
    @property
    def signature_algorithm(self) -> str:
        return self.x509.get_signature_algorithm().decode()
    @property
    def subject(self) -> str:
        return self.x509.get_subject().commonName
    @property
    def remaining(self) -> datetime.datetime:
        return self.not_after - datetime.datetime.now()
    @property
    def remaining_days(self) -> int:
        return int(self.remaining.days)
    @property
    def version(self) -> int:
        return self.x509.get_version()
cert = Certificate("example.com", 443)
print(f"Subject             : {cert.subject}")
print(f"Expired             : {cert.expired}")
print(f"Start               : {cert.not_before}")
print(f"End                 : {cert.not_after}")
print(f"Remaining           : {cert.remaining}")
print(f"Remaining Days      : {cert.remaining_days}")
print(f"Version             : {cert.version}")
print(f"Serial Number       : {cert.serial_number}")
print(f"Signature Algorithm : {cert.signature_algorithm}")
print(f"Issuer              : {cert.issuer}")
print(f"Public Key          : {cert.pubkey}")
  | 
 
実行例
実行結果は以下の通りです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20  | $ ./sample.py
Subject             : www.example.org
Expired             : False
Start               : 2023-01-13 09:00:00
End                 : 2024-02-14 08:59:59
Remaining           : 37 days, 9:11:04.842061
Remaining Days      : 37
Version             : 2
Serial Number       : 16115816404043435608139631424403370993
Signature Algorithm : sha256WithRSAEncryption
Issuer              : DigiCert TLS RSA SHA256 2020 CA1
Public Key          : -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwoB3iVm4RW+6StkR+nut
x1fQevu2+t0Fu6KBcbvhfyHSXy7w0nJOdTT4jWLjStpRkNQBPZwMwHH35i+21gdn
JtDe/xfO8IX9McFmyodlBUcqX8CruIzDv9AXf2OjXPBG+4aq+03XKl5/muATl32+
+301Vw1dXoGYNeoWQqLTsHT3WS3tOOf+ehuzNuZ+rj+ephaD3lMBToEArrtC9R91
KTTN6YSAOK48NxTA8CfOMFK5itxfIqB5+E9OSQTidXyqLyoeA+xxTKMqYfxvypEe
k1oueAhY9u67NCBdmuavxtfyvwp7+o6Sd+NsewxAhmRKFexw13KOYzDhC+9aMJcu
JQIDAQAB
-----END PUBLIC KEY-----
  |