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

使用Python为Zatca哈希XML文件与SDK结果不一致问题

ZATCA UBL 2.1 XML哈希结果与SDK不一致问题

我按照ZATCA官方要求处理UBL 2.1格式的XML发票哈希,但自己写的Python代码输出和官方SDK结果不匹配。根据ZATCA文档,正确的哈希步骤为:

  • 移除<Invoice>下的<ext:UBLExtensions/>块
  • 移除<invoice>下cbc:ID值为QR的<cac:AdditionalDocumentReference/>块
  • 移除<invoice>下的<cac:Signature/>块
  • 采用C14N11标准对处理后的Invoice做规范化
  • 用SHA256对规范化后的字符串生成二进制哈希值
  • 对二进制哈希值做Base64编码得到最终摘要

我的Python代码

f = open("invoice_00045.xml", "r")
st = f.read()
# Canonicalize
xmldsig = chilkat2.XmlDSig()
canonXml = xmldsig.CanonicalizeXml(st,"C14N",False)
encoded_str = canonXml.encode()
# create sha3-256 hash objects
obj_sha3_256 = hashlib.sha256(encoded_str)
hashed = obj_sha3_256.hexdigest()
# print in hexadecimal
encoded_base64_bytes = b64encode(bytes.fromhex(hashed)).decode()

输出对比

  • Python代码输出:
    R0J8+RX8HHdTiyjJSwstbKetQnI14KY1eugsBKsNXao=
    
  • SDK输出:
    LzcxChG74WtAw3jO3WQz9b+P44J0ivT/N97PLBELxYU=
    

问题分析与修正方案

你的代码存在几个关键错误,直接导致结果不一致:

  1. 未执行前三个节点移除步骤:代码直接读取原始XML就做规范化,完全跳过了ZATCA要求的节点删除操作,这是核心差异来源。
  2. 规范化标准错误:使用了C14N(C14N 1.0)而非要求的C14N11(C14N 1.1),两个标准的规范化规则有差异。
  3. 哈希流程冗余错误:SHA256哈希后先转十六进制字符串,再转回字节做Base64,这会改变原始二进制哈希值的结构。正确流程是直接对SHA256输出的二进制做Base64编码。
  4. 文件编码未指定:读取XML时未指定编码(比如UTF-8),可能导致字符解析不一致。

修正后的代码示例(使用lxml处理节点)

import lxml.etree as ET
import hashlib
import base64

# 读取XML并指定编码
tree = ET.parse("invoice_00045.xml")
root = tree.getroot()
ns = {
    'ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2',
    'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
    'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'
}

# 步骤1:移除<ext:UBLExtensions>块
for elem in root.xpath('./ext:UBLExtensions', namespaces=ns):
    root.remove(elem)

# 步骤2:移除cbc:ID为QR的<cac:AdditionalDocumentReference>块
for elem in root.xpath('./cac:AdditionalDocumentReference[cbc:ID="QR"]', namespaces=ns):
    root.remove(elem)

# 步骤3:移除<cac:Signature>块
for elem in root.xpath('./cac:Signature', namespaces=ns):
    root.remove(elem)

# 步骤4:C14N11规范化(根据ZATCA要求移除注释)
canon_xml = ET.tostring(root, method='c14n2', with_comments=False, encoding='utf-8')

# 步骤5-6:SHA256哈希+Base64编码
sha256_hash = hashlib.sha256(canon_xml).digest()
base64_result = base64.b64encode(sha256_hash).decode('utf-8')

print(base64_result)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:32:45