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

如何从p7s文件提取数字签名?两种场景Python实现求助

从P7S(PKCS#7/CMS格式)文件提取数字签名值的Python实现

核心逻辑:P7S文件中的原始签名值(即加密摘要)存储在CMS结构的SignerInfo.encrypted_digest字段,与被签名载荷是否内置无关,直接解析ASN.1结构即可同时适配载荷内置、载荷外置两种场景,无需依赖密码库高层封装的签名验证接口。

依赖安装

pip install asn1crypto

完整实现代码

from asn1crypto import cms
from typing import Optional, Dict, List

def extract_p7s_signature(p7s_path: str, detached_content_path: Optional[str] = None) -> Dict:
    """
    从p7s文件提取签名相关信息,同时适配载荷内置、载荷外置两种场景
    :param p7s_path: p7s签名文件路径
    :param detached_content_path: 外置载荷场景下的原文件路径,内置场景无需传入
    :return: 包含签名值、签名证书、载荷、签名属性的字典
    """
    # 读取p7s文件二进制内容
    with open(p7s_path, "rb") as f:
        p7s_data = f.read()
    
    # 解析CMS内容结构
    content_info = cms.ContentInfo.load(p7s_data)
    if content_info["content_type"].native != "signed_data":
        raise ValueError("输入文件不是合法的PKCS#7签名数据格式")
    
    signed_data = content_info["content"]
    result = {
        "signatures": [],  # 存储所有签名者的原始签名值(加密摘要)
        "signer_certs": [], # 存储签名者证书
        "signed_content": None, # 存储被签名的载荷内容
        "signer_attrs": [] # 存储签名者的其他属性(含时间戳等)
    }

    # 提取被签名载荷
    encap_content = signed_data["encap_content_info"]
    if encap_content["content"].native is not None:
        # 载荷内置场景:直接从p7s结构中取原内容
        result["signed_content"] = encap_content["content"].native
    elif detached_content_path is not None:
        # 载荷外置场景:读取外部原文件
        with open(detached_content_path, "rb") as f:
            result["signed_content"] = f.read()
    
    # 提取所有签名者的信息
    for signer_info in signed_data["signer_infos"]:
        # 核心:直接取encrypted_digest字段,即为需要的加密摘要(签名本身)
        signature_value = signer_info["encrypted_digest"].native
        result["signatures"].append(signature_value)

        # 提取签名者证书
        sid = signer_info["sid"]
        for cert in signed_data["certificates"]:
            if cert.native["tbs_certificate"]["serial_number"] == sid.native["serial_number"]:
                result["signer_certs"].append(cert)
                break
        
        # 提取签名属性(含签名时间戳等扩展信息)
        result["signer_attrs"].append(signer_info["signed_attrs"].native)
    
    return result

使用示例

# 场景1:载荷内置的p7s文件(p7s本身包含原文档内容)
attached_result = extract_p7s_signature("attached_doc.p7s")
print(f"提取到签名值(十六进制): {attached_result['signatures'][0].hex()}")
print(f"内置文档长度: {len(attached_result['signed_content'])}")

# 场景2:载荷外置的分离签名p7s文件(p7s不含原文档,需传入原文件路径)
detached_result = extract_p7s_signature("detached_sign.p7s", detached_content_path="original.pdf")
print(f"提取到签名值(十六进制): {detached_result['signatures'][0].hex()}")

关键说明

  • 之前使用PyOpenSSL、Cryptography等库无法获取签名值,是因为这类库的高层API将encrypted_digest字段封装在内部签名验证逻辑中,未暴露直接访问的属性,直接解析ASN.1结构不存在该限制
  • 外置签名场景下,提取原始签名值不需要传入原文件,传入原文件仅用于后续自行做签名校验使用
  • 提取到的签名值编码与签名算法匹配:RSA签名为私钥加密后的摘要原始字节,ECDSA签名为DER编码的(r, s)整数序列

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 13:19:07