【问题标题】:How to get the signed content from a PKCS7 envelop with M2Crypto?如何使用 M2Crypto 从 PKCS7 信封中获取签名内容?
【发布时间】:2013-03-29 09:54:43
【问题描述】:

我需要获取 PKCS#7 信封的摘要来手动检查它。

通常当您想要验证 pkcs#7 信封的签名时,您会这样做:

from M2Crypto import SMIME, X509, BIO

sm_obj = SMIME.SMIME()
x509 = X509.load_cert(join(PATH, 'QualifiedChain.crt'))
sk = X509.X509_Stack()
sk.push(x509)
sm_obj.set_x509_stack(sk)

st = X509.X509_Store()

st.load_info(join(PATH, 'QualifiedChain.crt'))

sm_obj.set_x509_store(st)

# re-wrap signature so that it fits base64 standards
cooked_sig = '\n'.join(raw_sig[pos:pos + 76] for pos in
                       xrange(0, len(raw_sig), 76))

# now, wrap the signature in a PKCS7 block
sig = "-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----\n" % cooked_sig


# and load it into an SMIME p7 object through the BIO I/O buffer:
buf = BIO.MemoryBuffer(sig)
p7 = SMIME.load_pkcs7_bio(buf)

signers = p7.get0_signers(sk)
certificat = signers[0]
data_bio = BIO.MemoryBuffer(MSG)
sm_obj.verify(p7, data_bio)  # This is the line that count.

但在我的例子中,摘要类型是 openssl 无法识别的 md5sha1:

$ openssl list-message-digest-commands
md4
md5
rmd160
sha
sha1

我需要做的是获取 pkcs#7 签名内容并手动检查它。

我需要的是一个相当于org.bouncycastle.cms.CMSSignedDataParser的Python。

如何让摘要能够手动验证而无需使用sm_obj.verify

【问题讨论】:

  • 我从未听说过“md5sha1”摘要,谷歌搜索似乎也没有任何结果。也许就像它读到的那样? SHA1 的 MD5?您从哪里得知摘要是 md5sha1?
  • 这是一些签名服务器的自定义摘要,这就是我需要手动验证它的原因。 md5sha1 只是 bash 哈希的连接。
  • 看看v13.gr/blog/?p=303。这可能是从 PKCS7 信封中获取内容和签名然后手动验证它的替代方法。
  • @likeitlikeit 看起来真不错。您介意为这篇文章创建一个答案并复制其中的代码吗?
  • @likeitlikeit 这是检查要由 cacert 签名的证书 无法检查 pkcs7 是否由 certficate 签名会尝试修改它。

标签: python m2crypto


【解决方案1】:

好的,所以我可以在 bash 中做到这一点:

#!/bin/bash

if [ $# -ne 1 ]; then
    echo "USAGE: $0 sig.pkcs7.pem"
    exit 1
fi

rm -fr /tmp/pkcs7tosignature
mkdir /tmp/pkcs7tosignature
cp "$1" /tmp/pkcs7tosignature/sig.pkcs7
cd /tmp/pkcs7tosignature/

# Convert PEM pkcs7 to DER
openssl pkcs7 -in sig.pkcs7 -inform PEM -out sig.der -outform DER

# Extract x509 certificate
openssl pkcs7 -in sig.pkcs7 -inform PEM -print_certs > cert.pem

# Look for signed signature offset
offset=$(openssl asn1parse -inform der -in sig.der | python -c "import sys; l = sys.stdin.readlines()[-1]; print int(l.split(':')[0]) + int(l.split('hl=')[1].split()[0])")
count=$(openssl asn1parse -inform der -in sig.der | python -c "import sys; l = sys.stdin.readlines()[-1]; print int(l.split('hl=')[1].split('l=')[1].split()[0])")

# Copy signed signature
dd if=sig.der of=signed-sha1.bin bs=1 skip=$[ $offset ] count=$count 2>/dev/null

# Extract public key from certificate
openssl x509 -inform pem -in cert.pem -noout -pubkey > pubkey.pem

# Decrypt signed signature
openssl rsautl -verify -pubin -inkey pubkey.pem < signed-sha1.bin > verified.bin

# Print pkcs7 algorithm
openssl asn1parse -inform der -in verified.bin | python -c "import sys; l = sys.stdin.read(); print l.split('OBJECT')[1].split('\n')[0].split(':')[1].strip()"

# Print pkcs7 signature
openssl asn1parse -inform der -in verified.bin | python -c "import sys; l = sys.stdin.read(); print l.split('[HEX DUMP]:')[1].split('\n')[0].strip()"

现在只需要在 Python 中转换它。

【讨论】:

【解决方案2】:

尝试以下,深受blog post 的启发:

import OpenSSL
from Crypto.Util import asn1

c=OpenSSL.crypto

certbuffer = # get your PEM here, e.g. by cert = open("/path/to/file.pkcs7.pem", 'rb').read()

# This is the certificate to validate
# an OpenSSL.crypto.X509 object
cert=c.load_certificate(crypto.FILETYPE_PEM, certbuffer)

# Get the signing algorithm
algo=cert.get_signature_algorithm()

# Get the ASN1 format of the certificate
cert_asn1=c.dump_certificate(c.FILETYPE_ASN1, cert)

# Decode the certificate
der=asn1.DerSequence()
der.decode(cert_asn1)

# The certificate has three parts:
# - certificate
# - signature algorithm
# - signature
# http://usefulfor.com/nothing/2009/06/10/x509-certificate-basics/
der_cert=der[0]
der_algo=der[1]
der_sig=der[2]

# The signature is a BIT STRING (Type 3)
# Decode that as well
der_sig_in=asn1.DerObject()
der_sig_in.decode(der_sig)

# Get the payload
sig0=der_sig_in.payload

# Do the following to see a validation error for tests
# der_cert=der_cert[:20]+'1'+der_cert[21:]

# First byte is the number of unused bits. This should be 0
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb540792(v=vs.85).aspx
if sig0[0]!='\x00':
    raise Exception('Number of unused bits is strange')

# Now get the signature itself
sig=sig0[1:]

# and do the actual verification against your custom hash
# ...

【讨论】:

  • 我无法验证这一点,因为我没有可用此结构的 PEM。如果@Natim你可以提供一个测试文件,我愿意尝试和改进,如果需要的话。
  • 问题是您的代码是用于证书签名而不是用于 pkcs7 签名。显然 pyOpenSSL 没有 PKCS7 的投标。
  • @Natim 你有测试文件吗?我想尝试使用 M2Crypto 从中提取签名。
  • 有什么线索吗?你到哪里去了?
  • @Natim 感谢您的提问。我再次尝试,非常努力,让这个工作。在阅读了 M2Crypto 的整个源代码(!)之后,我坚信不可能从 PKCS7 对象中获取证书。这主要用于编写 PKCS7 信封,并可能对其进行签名。 pyOpenSSL 肯定不能胜任这项任务,我也对它进行了深入研究。如果你也想尝试,我会选择pkcs7=m2.SMIME.load_pkcs7()der = m2.BIO.MemoryBuffer()pkcs7.write_der(der)。不幸的是,我无法从der 对象中获取证书,也无法解析它。
【解决方案3】:

PKCS7 有一组 SignerInfos。每个 SignerInfo 可能有不同的消息摘要算法。

https://github.com/erny/pyx509。这需要 pyasn1 和 pyasn1-modules。

./pkcs_parse <pkcs7 signature in DER format>

这会提取每个签名者信息的摘要算法。

【讨论】:

  • 你能帮我用 m2crypto 或密码学对数据进行签名(我的意思是相反的过程)吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 2013-07-13
  • 1970-01-01
  • 2012-11-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多