双向证书认证代替密码

7 条回复
65 次浏览

有时候自己搞个 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)

image

种子用户
OP

双向证书认证用的少,但是还是有用处的,我还有 ET199 实体密码狗的案例,还没发出来。

种子用户
OP

个人使用就很简单了,把证书到入手机和电脑里面就行了。只需要保管 CA 的私钥,个人的丢了再签就行了。

种子用户
OP

怎么方便怎么来吧,这里这是举例一个场景,重点在于双向证书认证。

发表一个评论

R保持