You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Windows 10的Python REST客户端中用YubiKey 5存储私钥实现SSL加密?

在Windows 10上用Python + YubiKey 5实现客户端证书认证的REST客户端

我刚好处理过类似的场景,直接在Python里用YubiKey的PKCS#11接口实现客户端证书认证完全可行,不用依赖ghostunnel这类中间层。下面是一步步的实现方案,针对Windows 10环境:

1. 前期准备

首先得确保你的YubiKey和Python环境配置到位:

  • 安装YubiKey工具链:下载并安装YubiKey Manager,它会自带PKCS#11驱动(ykcs11.dll),默认路径大概是C:\Program Files\Yubico\YubiKey Manager\ykcs11.dll
  • 安装Python依赖包:打开命令行运行:
    pip install python-pkcs11 cryptography requests pyopenssl
    
  • 导入证书和私钥到YubiKey:用YubiKey Manager或者ykman命令行工具,把你的客户端证书和对应私钥导入到YubiKey的PIV槽位(比如常用的认证槽位9c):
    # 导入私钥
    ykman piv import-key 9c your_private_key.pem
    # 导入证书
    ykman piv import-certificate 9c your_certificate.pem
    
    导入完成后,可以用ykman piv info确认证书和密钥是否存在。

2. 编写Python代码实现PKCS#11 SSL连接

核心思路是通过python-pkcs11调用YubiKey的PKCS#11接口获取私钥和证书,再结合pyopenssl构建自定义SSL上下文,最后让requests使用这个上下文发起请求。

以下是完整的示例代码:

import pkcs11
import getpass
from cryptography import x509
from cryptography.hazmat.primitives.serialization import Encoding
from OpenSSL import SSL, crypto
import requests

def create_pkcs11_ssl_context(pkcs11_lib_path, token_label, pin):
    # 加载YubiKey的PKCS#11库
    lib = pkcs11.lib(pkcs11_lib_path)
    # 获取YubiKey令牌(替换成你的YubiKey标签,可通过ykman list查看)
    token = lib.get_token(token_label=token_label)
    
    # 登录令牌
    with token.open(user_pin=pin) as session:
        # 从YubiKey获取私钥(对应导入的槽位标签,这里用默认的Authentication key)
        private_key = session.get_object(
            pkcs11.ObjectType.PRIVATE_KEY,
            label="Authentication key"
        )
        # 获取证书
        cert_obj = session.get_object(
            pkcs11.ObjectType.CERTIFICATE,
            label="Authentication certificate"
        )
        # 解析证书并转换成PEM格式
        cert_der = cert_obj[pkcs11.Attribute.VALUE]
        cert = x509.load_der_x509_certificate(cert_der)
        cert_pem = cert.public_bytes(Encoding.PEM)
        
        # 构建PyOpenSSL证书和私钥对象
        x509_cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert_pem)
        # 将PKCS#11私钥转换成PyOpenSSL可识别的格式
        from cryptography.hazmat.backends import openssl as openssl_backend
        backend = openssl_backend.backend
        crypto_pkey = backend.load_pkcs11_private_key(private_key, None)
        openssl_pkey = crypto.PKey.from_cryptography_key(crypto_pkey)
        
        # 创建SSL上下文
        ctx = SSL.Context(SSL.TLS_CLIENT_METHOD)
        ctx.use_certificate(x509_cert)
        ctx.use_privatekey(openssl_pkey)
        # 启用服务器证书验证(生产环境务必保留,可添加自定义CA证书)
        ctx.set_verify(
            SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
            lambda conn, cert, errnum, depth, ok: ok
        )
        return ctx

# 配置参数(根据你的实际情况修改)
PKCS11_LIB_PATH = "C:\\Program Files\\Yubico\\YubiKey Manager\\ykcs11.dll"
YUBIKEY_TOKEN_LABEL = "YubiKey PIV #1234"  # 替换成你的YubiKey标签
USER_PIN = getpass.getpass("Enter YubiKey PIV PIN: ")

# 创建适配PKCS11的requests会话
session = requests.Session()
ssl_ctx = create_pkcs11_ssl_context(PKCS11_LIB_PATH, YUBIKEY_TOKEN_LABEL, USER_PIN)

# 自定义适配器,让requests使用我们的SSL上下文
class PKCS11Adapter(requests.adapters.HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        kwargs["ssl_context"] = ssl_ctx
        return super().init_poolmanager(*args, **kwargs)

session.mount("https://", PKCS11Adapter())

# 发起REST请求
try:
    response = session.get("https://your-remote-server/api/your-endpoint")
    response.raise_for_status()
    print(f"请求成功,状态码: {response.status_code}")
    print("响应内容:", response.text)
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

3. 关键注意事项

  • PIN安全:绝对不要把PIN硬编码在代码里,示例中用getpass从控制台安全输入,生产环境可以考虑用环境变量或者安全密钥管理工具。
  • PKCS#11库路径:如果你的YubiKey Manager安装路径不同,要调整PKCS11_LIB_PATH,也可以使用OpenSC的opensc-pkcs11.dll,但YubiKey自带的ykcs11.dll兼容性更好。
  • 服务器证书验证:如果服务器使用自签名证书,你需要把CA证书添加到SSL上下文里,避免验证失败。可以用ctx.load_verify_locations("ca_certificate.pem")来加载自定义CA。
  • 槽位与标签:如果你导入证书和密钥时用了自定义槽位或标签,要修改代码中get_object的参数,确保能正确获取到对应的对象。

这个方案直接在Python代码中集成YubiKey的PKCS#11功能,完全避免了中间层带来的安全风险和部署复杂度,适合生产环境使用。

内容的提问来源于stack exchange,提问作者Avihai B

火山引擎 最新活动