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

