如何从Windows证书存储获取证书字节流直接用于requests请求?
问题
我有一个接收证书签名请求的API,目前用以下方式调用:
response = requests.post(some_url, data=some_data, cert=path_to_cert)
出于安全考虑,我不能把Windows证书存储里的证书下载到本地,必须从内存中发送请求。
有没有支持cert参数接收字符串/字节的库或requests适配器?
我尝试写了下面的代码,但卡在了把字符串/字节传入请求的步骤:
cert_name = "NAME CERT IN CERT STORE" def export_cert_to_pem(cert, pem_path)->str: pem = cert.get_pem() return pem def find_cert_in_trusted_roots(cert_name:str): for storename in ("CA", "ROOT"): with wincertstore.CertSystemStore(storename) as store: for cert in store.itercerts(usage=wincertstore.SERVER_AUTH): if cert.get_name().lower() == cert_name.lower(): export_cert_to_pem(cert, cert_name + ".pem") # export_cert_to_pem(cert, pem_path) return 1 return 0 cert = find_cert_in_trusted_roots(cert_name)
# 此处无法运行,cert参数必须传入路径
response = requests.post(some_url, data=your_data, cert=cert)
解决方案
标准requests库的cert参数仅支持文件路径字符串或证书/密钥路径元组,不直接支持内存中的字节/字符串。但可以通过自定义SSL适配器实现从内存加载证书。
方法:结合ssl.SSLContext与自定义requests适配器
先修改证书查找逻辑,同时获取证书和对应私钥的PEM字节数据(客户端证书需要证书+私钥配对):
import wincertstore import ssl import requests from requests.adapters import HTTPAdapter def find_cert_and_key(cert_name: str): # 私钥通常存放在MY存储区,需遍历相关存储 for storename in ("MY", "CA", "ROOT"): with wincertstore.CertSystemStore(storename) as store: for cert in store.itercerts(usage=wincertstore.SERVER_AUTH): if cert.get_name().lower() == cert_name.lower(): # 获取证书PEM字节 cert_pem = cert.get_pem() # 获取对应私钥PEM字节(需确保证书关联私钥且当前用户有权限访问) key_pem = cert.get_private_key().get_pem() return cert_pem, key_pem return None, None # 从证书存储获取内存中的证书和私钥 cert_pem, key_pem = find_cert_and_key("NAME CERT IN CERT STORE") if cert_pem and key_pem: # 创建SSL上下文并加载内存中的证书与私钥 ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) ssl_context.load_cert_chain( certfile=None, cert_data=cert_pem, keyfile=None, key_data=key_pem ) # 自定义适配器,将SSL上下文注入请求池 class MemoryCertAdapter(HTTPAdapter): def init_poolmanager(self, *args, **kwargs): kwargs["ssl_context"] = ssl_context return super().init_poolmanager(*args, **kwargs) # 初始化Session并挂载适配器 session = requests.Session() session.mount("https://", MemoryCertAdapter()) # 发送请求,无需传入cert参数 response = session.post(some_url, data=your_data) else: print("未找到目标证书或对应的私钥")
注意事项
- 证书对应的私钥通常存放在Windows的
MY存储区,需确保当前用户有权限访问该私钥。 - 如果私钥带有密码,需要额外处理解密逻辑,但通过
wincertstore获取的私钥一般已处于解密状态。
内容的提问来源于stack exchange,提问作者NonniSnon
相关产品推荐
相关产品推荐

