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

使用pyOpenSSL提取PE文件双签名时无法获取SHA256签名链

问题根因

不是pyOpenSSL不支持SHA256证书解析,是两处逻辑错误导致无法提取到第二组SHA256签名链:

  • 你对Windows PE双签名的结构理解有误:平台通用的SHA1+SHA256双签名,SHA256签名不是和SHA1签名并列平铺在PE安全目录中,而是作为外层SHA1签名PKCS7结构的未认证属性(unauthenticated attributes)存储的嵌套签名,现有代码仅解析了最外层PKCS7的证书列表,完全没有处理嵌套签名结构。
  • 现有get_certificates方法仅提取PKCS7结构顶层cert字段存储的证书,没有递归解析签名属性中嵌套的其他PKCS7结构(包括嵌套签名、时间戳签名),自然无法获取内层SHA256签名对应的证书链。
修复方案

1. 补充嵌套PKCS7提取逻辑

通过pyOpenSSL的底层FFI接口,遍历PKCS7签名信息的未认证属性,匹配微软定义的嵌套签名OID(1.3.6.1.4.1.311.2.4.1),提取内嵌的PKCS7对象,同时支持递归处理多层嵌套:

SPC_NESTED_SIGNATURE_OID = "1.3.6.1.4.1.311.2.4.1"

def get_nested_pkcs7(pkcs7_obj):
    from OpenSSL.crypto import _lib, _ffi, PKCS7
    nested_list = []
    if not pkcs7_obj.type_is_signed():
        return nested_list
    
    signer_info_set = _lib.PKCS7_get_signer_info(pkcs7_obj._pkcs7)
    signer_count = _lib.sk_PKCS7_SIGNER_INFO_num(signer_info_set)
    nested_nid = _lib.OBJ_txt2nid(SPC_NESTED_SIGNATURE_OID.encode())
    
    for idx in range(signer_count):
        signer_info = _lib.sk_PKCS7_SIGNER_INFO_value(signer_info_set, idx)
        attr = _lib.PKCS7_get_attribute(signer_info, nested_nid)
        if attr == _ffi.NULL:
            continue
        # 解析属性值中存储的嵌套PKCS7结构
        data_ptr = attr[0].value.set
        nested_p7 = _lib.d2i_PKCS7(_ffi.NULL, _ffi.new("unsigned char**", data_ptr), attr[0].value.length)
        if nested_p7 != _ffi.NULL:
            # 包装为pyOpenSSL的PKCS7对象
            py_p7 = PKCS7.__new__(PKCS7)
            py_p7._pkcs7 = nested_p7
            nested_list.append(py_p7)
            # 递归处理多层嵌套场景
            nested_list.extend(get_nested_pkcs7(py_p7))
    return nested_list

2. 补全PE安全目录遍历逻辑

部分PE文件会将多个签名以WIN_CERTIFICATE结构数组的形式并列存放在安全目录中,不要直接读取第一个签名块,要按结构长度遍历所有签名条目,避免漏读:

all_sig_blobs = []
offset = 0
while offset < ds_size:
    # WIN_CERTIFICATE头:dwLength(4B) + wRevision(2B) + wCertificateType(2B) = 8B
    entry_len = int.from_bytes(
        file_data[ds_address + offset : ds_address + offset + 4], 
        byteorder="little"
    )
    if entry_len <= 8:
        break
    sig_blob = file_data[ds_address + offset + 8 : ds_address + offset + entry_len]
    all_sig_blobs.append(sig_blob)
    offset += entry_len

3. 调整主逻辑递归解析所有层级证书

对所有读取到的签名块,先加载为PKCS7对象,再递归提取自身及嵌套结构中的所有证书:

all_certs = []
for sig_blob in all_sig_blobs:
    p7 = OpenSSL.crypto.load_pkcs7_data(OpenSSL.crypto.FILETYPE_ASN1, bytes(sig_blob))
    p7_queue = [p7]
    p7_queue.extend(get_nested_pkcs7(p7))
    for single_p7 in p7_queue:
        certs = get_certificates(single_p7)
        if certs:
            all_certs.extend(certs)

# 后续遍历all_certs输出证书信息即可
注意事项
  • 调用底层_lib、_ffi接口时,你之前用X509_dup避免段错误的处理是正确的,如果嵌套结构解析时出现内存访问错误,可参照相同思路对PKCS7对象做拷贝处理。
  • 如果需要完整提取所有时间戳证书,也可以参照嵌套签名的提取逻辑,匹配时间戳对应的OID做递归解析即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 04:09:30