You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python的requests调用Windows证书管理器证书发起签名POST请求?

如何用Windows证书管理器中的证书替代PFX文件发起requests签名POST请求

你当前的代码通过加载本地PFX文件实现了签名请求,但要改用Windows证书管理器(certmgr)中已安装的证书,需要直接访问Windows系统的证书存储,提取证书和私钥后供requests使用。以下是具体实现方案:

依赖安装

首先需要安装两个关键库,用于访问Windows证书存储和处理证书格式:

pip install pywin32 cryptography

修改后的完整代码

替换原有的Token类,新增从Windows证书存储加载证书的逻辑:

import requests
import base64
import contextlib
import tempfile
import win32crypt
import win32con
from cryptography import x509
from cryptography.hazmat.primitives import serialization

class Token():
    def __init__(self, urlAuthenticate, consumer_key, consumer_secret, cert_subject):
        self.urlAuthenticate = urlAuthenticate
        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret
        self.cert_subject = cert_subject  # 证书的主题名称,比如"CN=你的证书名称"

    @contextlib.contextmanager
    def carregarCertificadoDoWindows(self):
        # 打开当前用户的"个人"证书存储
        store = win32crypt.CertOpenStore(
            win32crypt.CERT_STORE_PROV_SYSTEM,
            0,
            None,
            win32crypt.CERT_SYSTEM_STORE_CURRENT_USER,
            "MY"
        )
        try:
            cert_context = win32crypt.CertEnumCertificatesInStore(store, None)
            found = False
            while cert_context:
                # 获取证书的主题名称(用于匹配)
                subject = win32crypt.CertGetNameString(
                    cert_context,
                    win32crypt.CERT_NAME_SIMPLE_DISPLAY_TYPE,
                    0
                )
                if subject == self.cert_subject:
                    # 导出证书为PEM格式
                    cert_der = win32crypt.CertGetCertificateContextProperty(
                        cert_context,
                        win32crypt.CERT_RAW_DATA_PROP_ID
                    )
                    cert = x509.load_der_x509_certificate(cert_der)
                    cert_pem = cert.public_bytes(serialization.Encoding.PEM)

                    # 导出私钥为PEM格式
                    key_prov_info = win32crypt.CertGetCertificateContextProperty(
                        cert_context,
                        win32crypt.CERT_KEY_PROV_INFO_PROP_ID
                    )
                    key_handle = win32crypt.CertOpenPrivateKey(
                        store,
                        key_prov_info,
                        0,
                        win32con.CRYPT_ACQUIRE_COMPARE_KEY_FLAG
                    )
                    key_blob = win32crypt.CryptExportKey(
                        key_handle,
                        None,
                        win32crypt.PKCS8_PRIVATE_KEY_INFO,
                        0,
                        None
                    )
                    private_key = serialization.load_der_private_key(
                        key_blob,
                        password=None
                    )
                    key_pem = private_key.private_bytes(
                        encoding=serialization.Encoding.PEM,
                        format=serialization.PrivateFormat.PKCS8,
                        encryption_algorithm=serialization.NoEncryption()
                    )

                    # 写入临时PEM文件(requests需要文件路径)
                    with tempfile.NamedTemporaryFile(suffix='.pem', delete=False) as t_pem:
                        t_pem.write(key_pem)
                        t_pem.write(cert_pem)
                        yield t_pem.name
                    found = True
                    break
                cert_context = win32crypt.CertEnumCertificatesInStore(store, cert_context)
            if not found:
                raise ValueError(f"未找到主题为{self.cert_subject}的证书")
        finally:
            win32crypt.CertCloseStore(store, 0)

    def getChave(self):
        chave = self.consumer_key + ':' + self.consumer_secret
        chave_bytes = chave.encode('utf8')
        chave_base64_bytes = base64.b64encode(chave_bytes)
        chave_base64_string = chave_base64_bytes.decode('utf8')
        return chave_base64_string

    def getHeaders(self, chave):        
        headers = {
            'Authorization': 'Basic ' + chave,
            'role-type': 'TERCEIROS',
            'content-type': 'application/x-www-form-urlencoded'
         }
        return headers

    def getDados(self):
        data = {'grant_type': 'client_credentials'}
        return data

    def gerarToken(self):
        headers = self.getHeaders(self.getChave())
        data = self.getDados()
        with self.carregarCertificadoDoWindows() as cert:
            requisicao = requests.post(
                url=self.urlAuthenticate,
                headers=headers,
                cert=cert,
                data=data
            )
            requisicao.raise_for_status()  # 抛出HTTP错误
            return requisicao.json()


# 使用示例:替换为你的证书主题名称
token = Token(
    'url/authenticate',
    'cbaaaa',
    'abcccccc',
    'CN=你的证书主题名称'
) 
print(token.gerarToken())

关键说明

  • 证书匹配方式:代码中通过证书的主题名称查找,你也可以改用证书指纹(更精准),只需替换获取和匹配的逻辑:用win32crypt.CertGetCertificateContextProperty(cert_context, win32crypt.CERT_SHA1_HASH_PROP_ID)获取指纹,再和目标指纹对比。
  • 证书存储位置:代码中访问的是当前用户的个人存储("MY"),如果证书在本地计算机存储,需要将win32crypt.CERT_SYSTEM_STORE_CURRENT_USER改为win32crypt.CERT_SYSTEM_STORE_LOCAL_MACHINE,且运行程序时需要管理员权限。
  • 私钥访问权限:确保运行程序的用户对证书的私钥有读取权限,否则会无法导出私钥。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:20:40