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

时间:2017-04-18 07:59:24

标签: python xml pycrypto xml-signature pyopenssl

我尝试使用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' :未声明的身份ifier 并找到了预编译的M2Crypto msi安装程序,但在运行时它会抛出以下错误: ImportError: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

1 个答案:

答案 0 :(得分:1)

标题中问题的答案为signxml

这是为所述目的设计的库。 PyOpenSSLM2Crypto在比XML签名低的对象上运行;验证后者将涉及规范化XML,摘要化XML的适当部分以及在摘要上比较提供的签名。尽管可能,但这并非不重要,并且为错误提供了很大的空间。例如,在您的PyOpenSSL代码中,您不会对签名值进行base64解码。

使用signxml,您的示例基本上是正确的。为了验证签名,您不需要私钥,因此您得到的错误与问题无关。通常,即使文件是经过PEM编码的,您也应该以二进制而非文本模式(open(filename, "rb"))阅读证书和密钥。

以下是一个有效的示例:

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对象在签名后已被序列化和反序列化,那么最简单的代码可能还不够; ({复杂的)详细信息在here中进行了说明。