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

Windows系统下Flask Web服务器如何使用证书存储中的SSL证书?

如何让Flask从Windows证书存储加载SSL证书?

确实,把SSL私钥直接存在文件系统里确实有安全风险——毕竟文件可能被意外读取、复制或者泄露。在Windows环境下,利用系统自带的证书存储来管理证书和私钥,无疑是更安全合规的方案。下面我就一步步教你怎么实现这个需求。

前置依赖安装

首先我们需要两个Python库来完成证书的读取和格式转换:

  • pywin32:用来访问Windows系统的证书存储API
  • cryptography:用来处理证书和私钥的格式转换,把系统存储里的二进制数据转换成Flask能识别的PEM格式

打开命令提示符,执行以下命令安装:

pip install pywin32 cryptography

核心实现代码

下面是完整的示例代码,我会加上详细注释,方便你理解每一步的作用:

import win32crypt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from cryptography import x509
from flask import Flask

app = Flask(__name__)

# 测试路由
@app.route('/')
def hello():
    return "Hello from Flask with Windows Cert Store SSL!"

def get_cert_and_key_from_store(cert_subject_name):
    """从Windows当前用户的个人证书存储中提取证书和私钥"""
    # 打开当前用户的"个人"证书存储(对应系统里的"MY"存储)
    store = win32crypt.CertOpenStore(
        win32crypt.CERT_STORE_PROV_SYSTEM,
        0,
        0,
        win32crypt.CERT_SYSTEM_STORE_CURRENT_USER | win32crypt.CERT_STORE_OPEN_EXISTING_FLAG,
        "MY"
    )
    
    # 根据证书的主题名称查找目标证书(比如你证书里的CN字段)
    cert_context = win32crypt.CertFindCertificateInStore(
        store,
        win32crypt.X509_ASN_ENCODING | win32crypt.PKCS_7_ASN_ENCODING,
        0,
        win32crypt.CERT_FIND_SUBJECT_STR,
        cert_subject_name,
        None
    )
    
    if not cert_context:
        raise ValueError(f"找不到主题名称为 {cert_subject_name} 的证书,请确认证书已导入当前用户的个人存储")
    
    # 提取证书的DER格式数据,转换为Flask需要的PEM格式
    cert_der = win32crypt.CertGetCertificateContextData(cert_context)
    cert = x509.load_der_x509_certificate(cert_der, default_backend())
    cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM)
    
    # 获取证书对应的私钥信息
    key_prov_info = win32crypt.CertGetCertificateContextProperty(
        cert_context,
        win32crypt.CERT_KEY_PROV_INFO_PROP_ID
    )
    
    # 获取私钥的句柄
    prov_handle = win32crypt.CryptAcquireContext(
        None,
        key_prov_info['pwszContainerName'],
        key_prov_info['pwszProvName'],
        key_prov_info['dwProvType'],
        win32crypt.CRYPT_ACQUIRE_SILENT_FLAG | win32crypt.CRYPT_VERIFYCONTEXT
    )
    
    # 导出私钥为DER格式,再转换为PEM格式
    # 注意:如果私钥设置为"不可导出",这一步会失败,需要调整证书私钥的权限
    key_blob = win32crypt.CryptExportKey(
        prov_handle,
        None,
        win32crypt.PKCS_PRIVATE_KEY_INFO,
        0,
        None
    )
    private_key = serialization.load_der_private_key(
        key_blob,
        password=None,
        backend=default_backend()
    )
    key_pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
    
    # 释放资源,避免内存泄漏
    win32crypt.CryptReleaseContext(prov_handle, 0)
    win32crypt.CertFreeCertificateContext(cert_context)
    win32crypt.CertCloseStore(store, 0)
    
    return cert_pem, key_pem

if __name__ == '__main__':
    # 替换成你的证书主题名称,比如 "CN=your-domain.com" 或者 "CN=localhost"
    target_cert_subject = "CN=localhost"
    try:
        # 从证书存储获取PEM格式的证书和私钥
        cert_pem, key_pem = get_cert_and_key_from_store(target_cert_subject)
        # 启动Flask,传入ssl_context参数
        # 注意:绑定443端口需要管理员权限,如果不想用管理员,换成8443之类的非特权端口
        app.run(host='0.0.0.0', port=443, ssl_context=(cert_pem, key_pem), debug=False)
    except Exception as e:
        print(f"加载证书或启动服务失败: {str(e)}")

关键注意事项

  • 私钥可导出权限:如果你的证书私钥设置为"不可导出",CryptExportKey步骤会报错。这时候你需要重新导入证书,或者在证书属性里调整私钥的导出权限(需要管理员权限)。
  • 管理员权限:绑定443、80这类默认端口需要管理员权限。如果不想用管理员,可以换成8443、8080这类非特权端口。
  • 证书存储位置:代码里打开的是当前用户的"个人"存储(MY),如果你的证书在本地计算机存储里,需要把CERT_SYSTEM_STORE_CURRENT_USER换成CERT_SYSTEM_STORE_LOCAL_MACHINE,并且运行程序时需要管理员权限。
  • 证书信任问题:如果你的证书是自签名或者企业CA颁发的,客户端访问时可能会提示证书不被信任,需要把根CA证书导入到客户端的信任存储里。

内容的提问来源于stack exchange,提问作者Jonesome Reinstate Monica

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:09:17