PythonでSSL証明書を扱うには下記2つのライブラリがあるようです。CSRの情報をpyOpenSSLで、サーバ証明書についてはCryptographyで取得してみました。時間がとれたら双方入れ替えて試してみたいと思います。
Get Information of CSR by pyOpenSSL
画面で入力されたCSRのテキスト情報(text)から、pyOpenSSLを利用してDNとキーの情報を取得してdictにまとめ、Flaskのテンプレートに渡しています。フローとしては以下のようになりますが、各オブジェクトの内容や用意されているメソッドは公式ドキュメントを参照してください。
- load_certificate_request()でCSRを読み取り、X509Reqオブジェクトを得る。
- X509Reqオブジェクトからget_subject()でDNの情報を取得する。X509Nameオブジェクトが得られるので、キーでループさせることでそれぞれの値を得ることができる。
- get_pubkey()を利用し、キーの情報をPKeyオブジェクトとして取得する。
コードは以下のような内容です。
import OpenSSL
from OpenSSL.crypto import load_certificate_request, FILETYPE_PEM
csr_info = {}
csr = load_certificate_request(FILETYPE_PEM, text) # get X509Req object
subject = csr.get_subject() # get X509Name object
dn = dict(subject.get_components()) # get DNs as dict
for key, value in dn.items():
csr_info[key.decode("utf-8")] = value.decode("utf-8")
key = csr.get_pubkey() # get PKey object
if key.type() == OpenSSL.crypto.TYPE_RSA:
key_type = "RSA"
else:
key_type = "DSA"
key_length = key.bits()
csr_info["key_type"] = key_type
csr_info["key_length"] = key_length
Get Information of Certificate by Cryptography
画面で入力されたサーバ証明書のテキスト情報(text)から、Cryptographyを利用してCommon NameやIssuerなどの情報を取得してdictにまとめています。なお、特定の情報だけを出力するためにOIDで属性を指定していますが、取得したNameオブジェクトに対して属性でループさせます。
- バイト型に変換したサーバ証明書の情報からload_pem_x509_certificate()でサーバ証明書を読み取り、Certificateオブジェクトを得る。
- subjectメソッドで証明書のNameオブジェクトを取得する。Nameオブジェクトからget_attributes_for_oid()を利用して必要な属性の値を得る。
- issuerメソッドで発行者のNameオブジェクトを得る。こちらもget_attributes_for_oid()を利用して必要な属性の値を取得できる。
- extensions.get_extension_for_class()を利用して証明書に設定されているSANsの情報を取得する。
- not_valid_beforeメソッドとnot_valid_afterメソッドを利用して有効期限を取得する。
コードは以下のような内容です。
from cryptography import x509
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend
crt_info = {}
cert = load_pem_x509_certificate(text.encode(), default_backend()) # get Certificate object
subject = cert.subject # get Name object
crt_info["common_name"] = subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value
issuer = cert.issuer # get Name object for Issuer
crt_info["issuer"] = issuer.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value
sans = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName) # get SANs
crt_info["sans"] = sans.value.get_values_for_type(x509.DNSName)
crt_info["start_date"] = cert.not_valid_before.strftime("%Y/%m/%d %H:%M:%S") # get start date and time UTC
crt_info["end_date"] = cert.not_valid_after.strftime("%Y/%m/%d %H:%M:%S") # get end date and time UTC
Notes
不正な証明書や情報が存在しない場合にはエラーになるため、細かい単位で try .. except によるExceptionへの対応が必要です。