大家注意:因为微信最近又改了推送机制,经常有小伙伴说错过了之前被删的文章,比如前阵子冒着风险写的爬虫,再比如一些限时福利,错过了就是错过了。
所以建议大家加个星标,就能第一时间收到推送!

文档
https://docs.python.org/zh-cn/3/library/socket.html
https://docs.python.org/zh-cn/3/library/ssl.html
openssl x509 -in .pem -noout -dates
echo | openssl s_client -servername -connect :443 2>/dev/null | openssl x509 -noout -dates
- # coding: utf-8
- # 查询域名证书到期情况
-
- import re
- import subprocess
- from datetime import datetime
- def get_re_match_result(pattern, string):
- match = re.search(pattern, string)
- return match.group(1)
-
- def parse_time(date_str):
- return datetime.strptime(date_str, "%b %d %H:%M:%S %Y GMT")
-
- def format_time(date_time):
- return datetime.strftime(date_time, "%Y-%m-%d %H:%M:%S")
-
- def get_cert_info(domain):
- """获取证书信息"""
- cmd = f"curl -Ivs https://{domain} --connect-timeout 10"
-
- exitcode, output = subprocess.getstatusoutput(cmd)
-
- # 正则匹配
- start_date = get_re_match_result('start date: (.*)', output)
- expire_date = get_re_match_result('expire date: (.*)', output)
-
- # 解析匹配结果
- start_date = parse_time(start_date)
- expire_date = parse_time(expire_date)
-
- return {
- 'start_date': start_date,
- 'expire_date': expire_date
- }
-
-
- def get_cert_expire_date(domain):
- """获取证书剩余时间"""
- info = get_cert_info(domain)
- print(info)
-
- expire_date = info['expire_date']
-
- # 剩余天数
- return (expire_date - datetime.now()).days
-
-
- if __name__ == "__main__":
- domain = 'www.baidu.com'
- expire_date = get_cert_expire_date(domain)
- print(expire_date)
核心代码
- # -*- coding: utf-8 -*-
-
- import socket
- import ssl
-
-
- def get_domain_cert(domain):
- """
- 获取证书信息
- :param domain: str
- :return: dict
- """
- socket.setdefaulttimeout(5)
-
- cxt = ssl.create_default_context()
- skt = cxt.wrap_socket(socket.socket(), server_hostname=domain)
-
- skt.connect((domain, 443))
- cert = skt.getpeercert()
-
- skt.close()
-
- return cert
-
-
- if __name__ == "__main__":
- print(get_domain_cert("www.baidu.com"))
还有一种方式也记录一下
- import socket
- import ssl
-
- def get_domain_cert(host, port=443, timeout=3):
- """
- 获取证书信息
- 存在问题:没有指定主机ip,不一定能获取到正确的证书信息
- :param host: str
- :param port: int
- :param timeout: int
- :return: dict
- """
- context = ssl.create_default_context()
-
- with socket.create_connection(address=(host, port), timeout=timeout) as sock:
- with context.wrap_socket(sock, server_hostname=host) as wrap_socket:
- return wrap_socket.getpeercert()
输出
- {
- 'subject': ((('countryName', 'CN'),), (('stateOrProvinceName', 'beijing'),), (('localityName', 'beijing'),), (('organizationalUnitName', 'service operation department'),), (('organizationName', 'Beijing Baidu Netcom Science Technology Co., Ltd'),), (('commonName', 'baidu.com'),)),
- 'issuer': ((('countryName', 'BE'),), (('organizationName', 'GlobalSign nv-sa'),), (('commonName', 'GlobalSign RSA OV SSL CA 2018'),)),
- 'version': 3,
- 'serialNumber': '4417CE86EF82EC6921CC6F68',
- 'notBefore': 'Jul 5 05:16:02 2022 GMT',
- 'notAfter': 'Aug 6 05:16:01 2023 GMT',
- 'subjectAltName': (('DNS', 'baidu.com'), ),
- 'OCSP': ('http://ocsp.globalsign.com/gsrsaovsslca2018',),
- 'caIssuers': ('http://secure.globalsign.com/cacert/gsrsaovsslca2018.crt',),
- 'crlDistributionPoints': ('http://crl.globalsign.com/gsrsaovsslca2018.crl',)
- }
结构化输出内容后的完整代码
- # -*- coding: utf-8 -*-
-
- import socket
- import ssl
-
- from dateutil import parser
-
- # requests.packages.urllib3.disable_warnings()
-
- try:
- _create_unverified_https_context = ssl._create_unverified_context
- except AttributeError:
- # Legacy Python that doesn't verify HTTPS certificates by default
- pass
- else:
- # Handle target environment that doesn't support HTTPS verification
- ssl._create_default_https_context = _create_unverified_https_context
-
- DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
-
- socket.setdefaulttimeout(5)
-
-
- def get_domain_ip(domain):
- """
- 获取ip地址
- :param domain: str
- :return: str
- """
- try:
- addrinfo = socket.getaddrinfo(domain, None)
- return addrinfo[0][-1][0]
- except Exception as e:
- pass
-
- return None
-
-
- def get_domain_cert(domain):
- """
- 获取证书信息
- :param domain: str
- :return: dict
- """
- cxt = ssl.create_default_context()
- skt = cxt.wrap_socket(socket.socket(), server_hostname=domain)
-
- skt.connect((domain, 443))
- cert = skt.getpeercert()
- skt.close()
-
- return cert
-
-
- def get_cert_info(domain):
- """
- 获取证书信息
- :param domain: str
- :return: dict
- """
- cert = get_domain_cert(domain)
-
- issuer = _tuple_to_dict(cert['issuer'])
- subject = _tuple_to_dict(cert['subject'])
-
- return {
- 'domain': domain,
- 'ip': get_domain_ip(domain),
- 'subject': _name_convert(subject),
- 'issuer': _name_convert(issuer),
- # 'version': cert['version'],
- # 'serial_number': cert['serialNumber'],
- 'start_date': _parse_time(cert['notBefore']),
- 'expire_date': _parse_time(cert['notAfter']),
- }
-
-
- def _tuple_to_dict(cert_tuple):
- """
- cert证书 tuple转dict
- :param cert_tuple: tuple
- :return:
- """
- data = {}
- for item in cert_tuple:
- data[item[0][0]] = item[0][1]
-
- return data
-
-
- def _name_convert(data):
- """
- 名字转换
- :param data: dict
- :return: dict
- """
- name_map = {
- 'C': 'countryName',
- 'CN': 'commonName',
- 'O': 'organizationName',
- 'OU': 'organizationalUnitName',
- 'L': 'localityName',
- 'ST': 'stateOrProvinceName'
- }
-
- dct = {}
- for key, value in name_map.items():
- dct[key] = data.get(value, '')
-
- return dct
-
-
- def _parse_time(time_str):
- """
- 解析并格式化时间
- :param time_str: str
- :return: str
- """
- return parser.parse(time_str).astimezone().strftime(DATETIME_FORMAT)
-
-
- if __name__ == "__main__":
- print(get_cert_info("www.baidu.com"))
输出
- {
- "domain": "www.baidu.com",
- "ip": "39.156.66.14",
- "subject": {
- "C": "CN",
- "CN": "baidu.com",
- "O": "Beijing Baidu Netcom Science Technology Co., Ltd",
- "OU": "service operation department",
- "L": "beijing",
- "ST": "beijing"
- },
- "issuer": {
- "C": "BE",
- "CN": "GlobalSign RSA OV SSL CA 2018",
- "O": "GlobalSign nv-sa",
- "OU": "",
- "L": "",
- "ST": ""
- },
- "start_date": "2022-07-05 13:16:02",
- "expire_date": "2023-08-06 13:16:01"
- }
该方式,不校验证书合法性,只获取证书信息
文档:https://pyopenssl.org/en/0.15.1/index.html#
依赖
pip install pyOpenSSL
示例
- # -*- coding: utf-8 -*-
-
- import ssl
- import OpenSSL
-
-
- def get_ssl_expire_date(host, port=443):
- cert = ssl.get_server_certificate((host, port))
- x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
- return x509.get_notAfter().decode()
-
-
- if __name__ == '__main__':
- print(get_ssl_expire_date('www.baidu.com'))
- # 20230806051601Z
项目地址:https://github.com/mouday/domain-admin
运行环境:Python 3.7.0
- $ pip install domain_admin
-
- # 升级到最新版本,可选
- $ pip3 install -U domain-admin -i https://pypi.org/simple
-
- # 启动运行
- $ gunicorn 'domain_admin.main:app'
访问地址:http://127.0.0.1:8000
默认的管理员账号:admin 密码:123456
时隔2个月,摸鱼学习交流群再次限时开放了。

Python技术交流群(技术交流、摸鱼、白嫖课程为主)又不定时开放了,感兴趣的朋友,可以在下方公号内回复:666,即可进入,一起 100 天计划!
老规矩,酱友们还记得么,右下角的 “在看” 点一下,如果感觉文章内容不错的话,记得分享朋友圈让更多的人知道!

- 【神秘礼包获取方式】
- 扫描下方二维码添加私人微信,再送一套精华电子书!,回复:电子书