python脚本

有时候我们会有一些需求,需要批量检查自己的域名证书的到期时间,用于提前告警续费,使用python可以轻松的完成这个任务;
这里有用到第三方库pip install -U cryptography,没有考虑站点重定向等情况,低版本python或cryptography需要改一些内容。

import socket
import ssl
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Sequence
from zoneinfo import ZoneInfo

from cryptography import x509

context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.load_default_certs()
context.check_hostname = True
context.verify_mode = ssl.CERT_REQUIRED


def get_certificate(domain, port=443, timeout=5) -> dict:
    """https://docs.python.org/3/library/ssl.html"""
    with socket.create_connection((domain, port), timeout=timeout) as _sock:
        with context.wrap_socket(_sock, server_hostname=domain) as sslsock:
            # cert = ssl.DER_cert_to_PEM_cert(sslsock.getpeercert(True))  # 证书加载为pem格式
            cert_bytes = sslsock.getpeercert(True)
            if cert_bytes is None:
                return {}

            certificate = x509.load_der_x509_certificate(cert_bytes)

            expired = certificate.not_valid_after_utc.astimezone(ZoneInfo("Asia/Shanghai"))
            created = certificate.not_valid_before_utc.astimezone(ZoneInfo("Asia/Shanghai"))

            return {
                "domain": domain,
                "version": certificate.version.name,
                "algorithm": certificate.signature_hash_algorithm.name,
                "subject": certificate.subject.rfc4514_string(),
                "issuer": certificate.issuer.rfc4514_string(),
                "expired_at": expired.strftime("%F %T"),
                "created_at": created.strftime("%F %T"),
            }


def multi_get_certificate(domains: Sequence[str]) -> list[dict]:
    result = []
    with ThreadPoolExecutor(max_workers=3) as pool:
        tasks = {pool.submit(get_certificate, domain): domain for domain in domains}
        for task in as_completed(tasks):
            domain = tasks[task]
            try:
                result.append(task.result(timeout=10))  # 10s强行断开
            except Exception as exc:
                print(f"{domain=}: failed to get certificate: {exc}")
    return result


if __name__ == "__main__":
    domains = ("baidu.com", "jd.com", "qq.com", "aliyun.com")
    print(multi_get_certificate(domains))

如果要检查的是本地证书只需要读取证书,传入x509.load_pem_x509_certificate方法即可。

脚本运行

输出以下内容

[
{"domain": "qq.com", "version": "v3", "algorithm": "sha256", "subject": "CN=qq.com,O=Shenzhen Tencent Computer Systems Company Limited,L=Shenzhen,ST=Guangdong Province,C=CN", "issuer": "CN=DigiCert Secure Site CN CA G3,O=DigiCert Inc,C=US", "expired_at": "2024-06-10 07:59:59", "created_at": "2023-05-10 08:00:00"}, 
{"domain": "jd.com", "version": "v3", "algorithm": "sha256", "subject": "CN=*.jd.com,O=BEIJING JINGDONG SHANGKE INFORMATION TECHNOLOGY CO.\\, LTD.,L=Beijing,ST=Beijing,C=CN", "issuer": "CN=GlobalSign RSA OV SSL CA 2018,O=GlobalSign nv-sa,C=BE", "expired_at": "2024-12-09 09:34:41", "created_at": "2023-11-08 09:34:42"}, 
{"domain": "baidu.com", "version": "v3", "algorithm": "sha256", "subject": "CN=www.baidu.cn,O=BeiJing Baidu Netcom Science Technology Co.\\, Ltd,ST=\u5317\u4eac\u5e02,C=CN", "issuer": "CN=DigiCert Secure Site Pro CN CA G3,O=DigiCert Inc,C=US", "expired_at": "2025-03-02 07:59:59", "created_at": "2024-01-30 08:00:00"}, {"domain": "aliyun.com", "version": "v3", "algorithm": "sha256", "subject": "CN=*.aliyun.com,O=Alibaba (China) Technology Co.\\, Ltd.,L=HangZhou,ST=ZheJiang,C=CN", "issuer": "CN=GlobalSign Organization Validation CA - SHA256 - G3,O=GlobalSign nv-sa,C=BE", "expired_at": "2024-12-30 16:21:02", "created_at": "2023-12-07 15:21:04"}
]