如何用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
相关产品推荐
相关产品推荐

