未能使用m2crypto验证DSA公钥的dsawithSha1签名消息
我正在尝试使用一个包含DSA公钥的x509证书来验证一个签名消息。这个x509证书是由一个SAP系统提供的,采用PKCS7编码。经过使用openssl转换成PEM格式后,我可以读取证书的内容(使用命令openssl x509 -in sapcert.pem -inform pem -text)。证书中包含了一个DSA加密的公钥,显示了DSA的参数y(公钥)、p、q和g。
因为在M2Crypto的x509类中没有找到DSA的实现,我尝试自己构建DSA公钥。为此,我对MyCrypto进行了修改(可以参考这个链接:如何根据参数和密钥值创建M2Crypto DSA对象?),编译后得到了一个新函数DSA.pub_key_from_params(p,q,g,y),用来根据证书中的参数构建我的DSA公钥。到目前为止,一切都正常运行。(单元测试也没有错误)。
在第二步,我通过URL参数接收了签名消息(称为seckey),在解码后(使用base64)得到了一个可以用openssl读取的DER字符串(使用命令openssl ans1parse -in seckey -inform der)。在openssl的输出中,我可以看到签名的消息摘要,这正是我想要验证的SHA1编码的消息摘要(所以我可以确认签名消息是正确提供的)。虽然我可以看到一个dsaWithSHA1的签名字符串,其中似乎包含了验证DSA签名消息所需的r和s值(可以参考这个链接:M2Crypto:验证DSA签名)。
到目前为止,我在尝试验证签名消息的过程中卡住了几天,希望有加密专家能帮我。我尝试了很多方法,也搜索了很多资料,甚至尝试了pyCrypto库,但都没有成功。
我尝试将SHA1消息摘要和r、s值传递给M2Crypto.DSA.verify函数,但由于失败,我认为我可能需要传递签名消息或其部分内容。(在一个Java论坛上,我看到一些关于验证SAP提供的签名消息的帖子,其中提到过“对签名属性的DER编码进行签名计算”之类的内容?)
这是我的示例代码:
# -*- coding: iso-8859-1 -*-
import M2Crypto
import urllib
import base64
from Crypto.Util import asn1
from M2Crypto import m2
import sha
# the certificate
cert = """subject=/C=DE/O=SAP Trust Community/OU=SAP Web AS/OU=I0020154766/CN=RE2
issuer=/C=DE/O=SAP Trust Community/OU=SAP Web AS/OU=I0020154766/CN=RE2
-----BEGIN CERTIFICATE-----
MIICMDCCAe8CAQAwCQYHKoZIzjgEAzBkMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
U0FQIFRydXN0IENvbW11bml0eTETMBEGA1UECxMKU0FQIFdlYiBBUzEUMBIGA1UE
CxMLSTAwMjAxNTQ3NjYxDDAKBgNVBAMTA1JFMjAeFw05NzEwMDEwMDAwMDBaFw0z
ODAxMDEwMDAwMDBaMGQxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNTQVAgVHJ1c3Qg
Q29tbXVuaXR5MRMwEQYDVQQLEwpTQVAgV2ViIEFTMRQwEgYDVQQLEwtJMDAyMDE1
NDc2NjEMMAoGA1UEAxMDUkUyMIHyMIGpBgcqhkjOOAQBMIGdAkEA//8x1Bqn4a00
FKr9CTwPPskxy0yrx7iU6T4vza4wW93Mo2d/IYTZNAxFqhrm+fIUrEp5fxIpYRmJ
rKL2qRCUmQIVANrcsXlFvrXH455gM69vKZebhQZfAkEAmYzXTzHYwvqKEM46FvQX
yC5O+JInwgk7Dac7gqGAkkhCS1aII4Vkc9kIEx3GFLD2mx4+yJuMQ8pQ4wz4FkfB
JANEAAJBAI1em/0owxMTEP+akz56BovQ7Q6LiqUmVLLxJcjDozjI+5z6IrAPtub2
veLXPdghDHcHB5jHKoqT4JHpqRc+uhIwCQYHKoZIzjgEAwMwADAtAhRm2jqiMWL+
26mA7HdKfZdkawMuYQIVAMekXdAT4wbyrb5/yFtuIPjCBfpr
-----END CERTIFICATE-----
"""
f = open('sapcert.pem', 'w')
f.write(cert)
f.close()
# now you can see it content with openssl
# openssl x509 -in sapcert.pem -inform pem -text
# this is the signedMessage
secKey = "MIIBSwYJKoZIhvcNAQcCoIIBPDCCATgCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHATGCARcwggETAgEBMGkwZDELMAkGA1UEBhMCREUxHDAaBgNVBAoTE1NBUCBUcnVzdCBDb21tdW5pdHkxEzARBgNVBAsTClNBUCBXZWIgQVMxFDASBgNVBAsTC0kwMDIwMTU0NzY2MQwwCgYDVQQDEwNSRTICAQAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTExMDUyNjE1MzAyNVowIwYJKoZIhvcNAQkEMRYEFPelg4iVtaKORpuFxUvgo23Du7%2BtMAkGByqGSM44BAMELjAsAhQ46oCNmzZArb5yOFSYGY0hWu8dZwIUT35hPccJ6B9HIsOE0u8LwYZaFNk%3D"
secKey = urllib.unquote(secKey)
secKey64 = base64.b64decode(secKey)
# now you can save it as a DER encoded file
f = open('seckey.der', 'wb')
f.write(secKey64)
f.close()
# you can pass it to openssl to see the the content
# openssl asn1parse -in seckey.der -inform der
# here is the sha1 encoded messagedigest I had to verify
hashstr = "ZS4DDB9616BA031C40E1008003AC100097dCN%3DRE2,OU%3DI0020154766,OU%3DSAPWebAS,O%3DSAPTrustCommunity,C%3DDE20110526173025"
osha1=M2Crypto.EVP.MessageDigest('sha1')
osha1.update(hashstr)
sha1_md = osha1.digest()
print sha1_md.encode('hex')
# now i build a DSA key with the params found in the certificate
pub="8d5e9bfd28c3131310ff9a933e7a068bd0ed0e8b8aa52654b2f125c8c3a338c8fb9cfa22b00fb6e6f6bde2d73dd8210c77070798c72a8a93e091e9a9173eba12"
p="ffff31d41aa7e1ad3414aafd093c0f3ec931cb4cabc7b894e93e2fcdae305bddcca3677f2184d9340c45aa1ae6f9f214ac4a797f1229611989aca2f6a9109499"
q="dadcb17945beb5c7e39e6033af6f29979b85065f"
g="998cd74f31d8c2fa8a10ce3a16f417c82e4ef89227c2093b0da73b82a1809248424b568823856473d908131dc614b0f69b1e3ec89b8c43ca50e30cf81647c124"
pub1 = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(pub))
p1 = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(p))
q1 = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(q))
g1 = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(g))
# this function is available after patching und compiling M2Crypto
# https://bugzilla.osafoundation.org/attachment.cgi?id=5700
dsa1 = M2Crypto.DSA.pub_key_from_params(p1, q1, g1, pub1)
print dsa1.check_key()
# this seems to be the signature-values r and s in seckey.der
asn_hex = "302C021438EA808D9B3640ADBE72385498198D215AEF1D6702144F7E613DC709E81F4722C384D2EF0BC1865A14D9"
r = asn_hex[8:48]
s = asn_hex[52:]
r1 = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(r))
s1 = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.hex_to_bn(s))
# but this fails
v = dsa1.verify(sha1_md, r1, s1)
print v
# this too
sk = M2Crypto.m2.bn_to_mpi(M2Crypto.m2.bin_to_bn(secKey64))
v = dsa1.verify(sk, r1, s1)
print v
有没有人知道如何正确进行dsaWithSHA1签名验证?请帮忙!最好的问候,Falko
1 个回答
你在示例代码中提到的 seckey64
实际上是一个来自 CMS 的签名数据结构。
它包含两个“签名属性”,分别是签名时间和消息摘要。为了验证这个签名,你需要遵循RFC中关于至少有一个签名属性时的规则。
在你的情况下,原始数据的消息摘要包含在消息摘要属性中,所以你需要先逐字节比较你计算的值和这个属性的值。
如果它们相等,那么你就可以从SignerInfo结构的相应字段中提取签名值。最后,你的SHA-1消息摘要将作为DSA签名验证的另一个输入,这个摘要是通过对SignerInfo的签名属性的原始编码进行计算的。我不确定M2Crypto是否内置支持SignedData(它仍然常被称为PKCS#7),但我猜 pyOpenSSL 应该支持它,所以如果M2Crypto让你遇到困难,可以试试这个。