双向证书认证代替密码
有时候自己搞个 web 服务给自己用,又懒得用账密,毕竟账密记得太多,不如试试双向证书认证,自建 CA 自己签发证书,用证书去认证。
用 nginx 绑定证书,后端读取用户发来的证书验证签发者和一些信息就可以当做身份信息了。
下面是 Python 读取证书信息的方法
复制
# -*- coding: utf-8 -*-
"""
@Time : 2024-09-26 14:25
@Auth : mxpy
@File :ca_util.py
@IDE :PyCharm
@Email:xxx
"""
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
from datetime import datetime
# 加载证书
def load_cert(cert_path):
with open(cert_path, 'rb') as f:
return x509.load_pem_x509_certificate(f.read(), default_backend())
# 加载 CA 证书
def load_ca_cert(ca_cert_path):
with open(ca_cert_path, 'rb') as f:
return x509.load_pem_x509_certificate(f.read(), default_backend())
# 加载 客户端 证书
def load_client_cert(client_path):
with open(client_path, 'rb') as f:
return x509.load_pem_x509_certificate(f.read(), default_backend())
def check_cert(ca_path, client_path):
# 解码证书
# cert_bytes = client_cert.replace("\t", "").encode('utf-8')
# cert = x509.load_pem_x509_certificate(cert_bytes, default_backend())
ca_cert = load_cert(ca_path)
cert = load_cert(client_path)
# 获取 CN
cn = cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value
# 验证证书是否由 CA 签发
try:
# 验证证书
ca_cert.public_key().verify(
cert.signature,
cert.tbs_certificate_bytes,
padding.PKCS1v15(),
cert.signature_hash_algorithm
)
is_signed_by_ca = True
except Exception as e:
is_signed_by_ca = False
print(f"Verification error: {e}")
# 获取颁发机构
issuer = cert.issuer
issuer_info = {attr.oid._name: attr.value for attr in issuer}
issuer_cn = cert.issuer.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value
issued_time = cert.not_valid_before # 颁发时间
# 尝试获取备用名称
san = []
try:
san_extension = cert.extensions.get_extension_for_oid(x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
# 获取 DNS 名称和 IP 地址
for name in san_extension.value:
if isinstance(name, x509.DNSName):
# san.append({'type': 'DNS', 'value': name.value})
san.append(name.value)
elif isinstance(name, x509.IPAddress):
# san.append({'type': 'IP', 'value': name.value})
# san.append(name.value) # IPv4Address
san.append(str(name.value)) #直接显示 IP
except x509.ExtensionNotFound:
san = [] # 扩展不存在时返回空列表
# 检查证书是否过期
current_time = datetime.utcnow()
is_valid = cert.not_valid_before <= current_time <= cert.not_valid_after
print(f"证书持有人:{cn}")
print(f"证书可选备用名称:{san}")
print(f"证书颁发时间:{issued_time.isoformat()}")
print(f"证书是否有效:{is_valid}")
print(f"证书颁发机构:{issuer_cn}")
print(f"证书颁发机构校验:{is_signed_by_ca}")
if __name__ == '__main__':
# client_cert_path = r"C:\Users\xxx\client-mxpy.crt"
client_cert_path = r"C:\Users\xxx\192.168.0.166.crt"
ca_cert_path = r"C:\Users\xxx\ca.crt"
# load_ca_cert(ca_cert)
# load_client_cert(client_cert)
check_cert(ca_cert_path,client_cert_path)

简直是折腾达人 😶🌫️