在python中验证XML签名的最佳方法是什么?



我正在尝试用python中的给定公共密钥验证XML消息签名,该密钥在使用openssl的php代码上进行了验证。

这是正常工作的PHP代码。

$pubKey = openssl_pkey_get_public(file_get_contents("public_key.pem"));
$xmlDoc = new DOMDocument();
$xmlDoc->load("message.xml");
$signedInfo=$xmlDoc->getElementsByTagName("SignedInfo")->item(0)->C14N(true, true);
$signature = base64_decode($xmlDoc->documentElement->getElementsByTagName("SignatureValue")->item(0)->nodeValue);
$ok = openssl_verify($signedInfo, $signature, $pubKey, OPENSSL_ALGO_SHA1);

我在Python中找到了不同的库来实现这一目标,但是没有一个可以验证。我已经列出了库以及我面临的问题。还有其他首选方法可以实现这一目标吗?

1。pyopenssl

它失败了以下消息:[('rsa例程','int_rsa_verify','错误签名长度'(]

import OpenSSL.crypto as c
from StringIO import StringIO
import xml.etree.ElementTree as xml_et
from myapp import settings
namespace = "{http://www.w3.org/2000/09/xmldsig#}"
xml_bytes = open(settings.STATIC_ROOT + '/file/test.xml', 'rt').read()
response_xml = xml_et.fromstring(xml_bytes.encode('utf-8'))
signature_elem = response_xml.find(namespace + 'Signature')
signature_value = signature_elem.find(namespace + 'SignatureValue').text
signed_info_output = StringIO()
signed_info_tree = xml_et.ElementTree(signature_elem.find(namespace + 'SignedInfo'))
signed_info_tree.write_c14n(signed_info_output)
signed_info = signed_info_output.getvalue()
# load certificate
cert = c.load_certificate(c.FILETYPE_PEM, open(settings.STATIC_ROOT + '/file/public.cert', 'rt').read())
# verify signature
try:
    c.verify(cert, signature_value, signed_info, 'sha1')
    print 'success'
except Exception, e:
    print 'fail'

2。m2crypto

尝试安装M2Crypto,但由于无法找到openssl/err.h头文件而失败。因此,我已经安装了OpenSSL 1.1.0E并复制了LIB,并将目录包含到C:/pkg目录,并引发不同的错误,例如:swig/_m2crypto_wrap.c(3754(:错误c2065:'crypto_num_locks':undeclared Identifier并找到了预编译的M2Crypto MSI安装程序,但在运行时,它会在错误之后引发错误:Invimperror:DLL负载失败:找不到指定的模块。

这个库似乎已经过时了,没有足够的文档。

3。signxml

到目前为止,这是唯一对我部分工作的图书馆。XML验证效果很好,但在符号上引发错误:ValueError:无法删除关键数据。

from xml.etree import ElementTree
from signxml import XMLSigner, XMLVerifier
from myapp import settings
cert = open(settings.STATIC_ROOT + '/file/public.cert', 'rt').read()
key = open(settings.STATIC_ROOT + '/file/public.key', 'rt').read()
root = ElementTree.fromstring('<xml1>12</xml1>')
signed_root = XMLSigner().sign(root, key=key, cert=cert)
verified_data = XMLVerifier().verify(signed_root).signed_xml
print verified_data

标题中问题的答案是 signxml

是为既定目的而设计的库。PyOpenSSLM2Crypto比XML签名在较低级别的对象上操作;验证后者将涉及规范化XML,消化其适当的部分,并比较消化中提供的签名。虽然可能,但这并不是微不足道的,并且为错误提供了很多空间。例如,在PyOpenSSL的代码中,您不base64 decode the签名值。

使用signxml,您的示例主要是正确的。要验证签名,您不需要私钥,因此您获得的错误与问题无关。通常,您应该以二进制读取证书和密钥,而不是文本模式(open(filename, "rb")( - 即使文件是pem编码的。

以下是一个工作示例:

from xml.etree import ElementTree
from signxml import XMLSigner, XMLVerifier
cert = open("cert.pem", "rb").read()
key = open("key.pem", "rb").read()
xml_obj = ElementTree.fromstring("<Example/>")
signed_xml_obj = XMLSigner().sign(xml_obj, key=key)
XMLVerifier().verify(signed_xml_obj, x509_cert=cert)

如果您的XML对象在签名后已被序列化和去序列化,则此最简单的代码可能还不够。此处描述了(复杂的(细节。

相关内容

最新更新