需要使用 GOST 34.11 算法来计算哈希值。文档中有一个计算示例:
<ds:Reference URI="#id-9a89bcea-eb0d-41f2-a8e8-405ad5ea0d62">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>STvOHkqaWjQbVCkB7mnG++RLLdGmjryqTqS+BcZx4Cg=</ds:DigestValue>
</ds:Reference>
所引用文件的范围:
<a:To wsu:Id="id-9a89bcea-eb0d-41f2-a8e8-405ad5ea0d62" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">https://ips.test.egisz.rosminzdrav.ru/57234d87b0838</a:To>
我还没有弄清楚要使用什么算法来获取哈希值(2012 年 11 月 34 日或 1994 年 11 月 34 日)。以下是 2012 年 11 月 34 日的代码(pygost 库:https://github.com/ilyaTT/pygost_0_15/tree/master):
from lxml import etree
from pygost.gost3411_12 import GOST341112
import base64
data = '''<a:To wsu:Id="id-9a89bcea-eb0d-41f2-a8e8-405ad5ea0d62" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:a="http://www.w3.org/2005/08/addressing">https://ips.test.egisz.rosminzdrav.ru/57234d87b0838</a:To>'''
parser = etree.XMLParser(remove_blank_text=True)
xml_tree = etree.fromstring(data.encode("utf-8"), parser)
canonical_xml = etree.tostring(xml_tree, method="c14n", exclusive=True).decode("utf-8")
canonical_base64 = base64.b64encode(canonical_xml.encode("utf-8")).decode("utf-8")
data_bytes = canonical_base64.encode("utf-8")[::-1]
# Хэшируем
gost_hash = GOST341112(digest_size=256)
gost_hash.update(data_bytes)
signature_hash = gost_hash.digest()[::-1]
# Хэш в шестнадцатеричной строке
hex_string = signature_hash.hex()
print("HEX:", hex_string)
new_value = base64.b64encode(signature_hash).decode()
print("BASE64:", new_value)
程序中获得的哈希值与样本中的哈希值不匹配。
GOST 34.11-1994 的实施:
from lxml import etree
from pygost.gost3411_94 import GOST341194
import base64
# Исходный XML-документ
xml_data = '''<a:To wsu:Id="id-9a89bcea-eb0d-41f2-a8e8-405ad5ea0d62" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">https://ips.test.egisz.rosminzdrav.ru/57234d87b0838</a:To>'''
root = etree.fromstring(xml_data)
# Канонизируем (c14n)
data_to_hash = etree.tostring(root, method="c14n", exclusive=True, with_comments=False)
print(type(data_to_hash))
print(data_to_hash)
# Вычисляем хэш (ГОСТ Р 34.11-94)
gost_hash = GOST341194(data_to_hash)
digest_value = gost_hash.digest()
digest_base64 = base64.b64encode(digest_value).decode()
print("DigestValue:", digest_base64)
得到了哈希:“ost9NriBuOVw2DKHUiKThCpIVkk2HITA0v6cMWv16mU=”但它应该是:“STvOHkqaWjQbVCkB7mnG++RLLdGmjryqTqS+BcZx4Cg=”
http://www.w3.org/2001/04/xmldsig-more#gostr3411
标识 GOST R 34.11-94,例如:https ://datatracker.ietf.org/doc/html/draft-chudov-cryptopro-cpxmldsig-00 (请注意,这是一个过时的标识符,但算法没那么过时。但是,如果某个地方的某个人仍在使用它,那么如果可能的话,它值得使用urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr3411
)GOST R 34.11-2012 具有标识符
urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256
和urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-512
,例如:https://datatracker.ietf.org/doc/html/draft-smirnov-xmldsig-05附言
这很奇怪,我甚至会说这是错的,因为测试参数仅用于测试(从 GOST R 34.11-94 开始到处都有写),如果没有明确要求,不应使用。
虽然,如果这个库中没有其他严重的错误,它可能并不致命,但是在您的代码中:规范指出:
附言。
pygost 值哈希被反转,您可以将其与 OpenSSL 测试用例进行比较:github.com/gost-engine/engine/blob/v3.0.3/test/01-digest.t
据我了解,已确认问题中的代码在处理时
<Reference URI=...>
不会输出整个标签,而只会输出其内容。