需求:在python下实现pkcs7格式的数字签名。
环境:Python2.7, pyopenssl-16.2.0 , cryptography 1.8.1 。
实现方法:参考http://stackoverflow.com/questions/33634379/pkcs-7-detached-signature-with-python-and-pyopenssl
注意:这个办法只能默认用sha256算法。2015年传出SHA1不安全的问题后, cryptography的PKCS7_sign 方法默认使用SHA256算法,且目前版本无法切换到SHA1(因为不支持PKCS7_sign_add_signer
。cryptography旧版本可能使用的SHA1,但官网找半天没找到下载)。
#!/usr/bin/env python
# coding=utf-8
# pkcs7格式签名
# http://stackoverflow.com/questions/33634379/pkcs-7-detached-signature-with-python-and-pyopenssl
import base64
from OpenSSL import crypto
def sign_p7():
f = open(r'../../temp/msa.pfx','rb')
pfx_buffer = f.read()
p12 = crypto.load_pkcs12(pfx_buffer,'1234')
signcert = p12.get_certificate()
pkey = p12.get_privatekey()
bio_in = crypto._new_mem_buf('xxx')
# define PKCS7_TEXT 0x1
# define PKCS7_NOCERTS 0x2
# define PKCS7_NOSIGS 0x4
# define PKCS7_NOCHAIN 0x8
# define PKCS7_NOINTERN 0x10
# define PKCS7_NOVERIFY 0x20
# define PKCS7_DETACHED 0x40
# define PKCS7_BINARY 0x80
# define PKCS7_NOATTR 0x100
# define PKCS7_NOSMIMECAP 0x200
# define PKCS7_NOOLDMIMETYPE 0x400
# define PKCS7_CRLFEOL 0x800
# define PKCS7_STREAM 0x1000
# define PKCS7_NOCRL 0x2000
PKCS7_TEXT = 0x1
PKCS7_NOSIGS = 0x4
PKCS7_DETACHED = 0x40
PKCS7_NOATTR = 0x100
PKCS7_NOSMIMECAP = 0x200
PKCS7_PARTIAL = 0x4000
# 默认使用SHA256算法,暂未找到方法切换到SHA1
pkcs7 = crypto._lib.PKCS7_sign(signcert._x509, pkey._pkey, crypto._ffi.NULL, bio_in, PKCS7_DETACHED|PKCS7_NOATTR)
bio_out = crypto._new_mem_buf()
crypto._lib.i2d_PKCS7_bio(bio_out, pkcs7)
sigbytes = crypto._bio_to_string(bio_out)
sigb64 = base64.b64encode(sigbytes)
print sigb64
f = open(r'sign_logon_bin.p7b', 'wb')
f.write(sigbytes)
f.close()
#SignedData = base64.urlsafe_b64encode(sigbytes)
SignedData = base64.b64encode(sigbytes)
print "SignedData = " + SignedData
f = open(r'sign_logon_b64.p7b','w')
f.write(SignedData)
f.close()
if __name__ == "__main__":
print "sign start..."
sign_p7()
print "sign end..."
如不要求pkcs7格式,可这样(可使用SHA1):
#!/usr/bin/env python
# coding=utf-8
import base64
from OpenSSL import crypto
from OpenSSL.crypto import load_certificate, load_privatekey
def sign_verify():
f = open(r'../../temp/msa.cer', 'r')
cert_buffer = f.read()
f.close()
# print "cert_buffer = " + cert_buffer
x509 = load_certificate(crypto.FILETYPE_PEM, cert_buffer)
pubkey = x509.get_pubkey()
f = open(r'../../temp/msa_pkcs8.key', 'r')
pkey_buffer = f.read()
f.close()
# print "pkey_buffer = " + pkey_buffer
privkey = load_privatekey(crypto.FILETYPE_PEM, pkey_buffer)
tosign = "xxxx"
sign_data_bin = crypto.sign(pkey=privkey, data=tosign, digest='sha1')
sign_data = str(base64.standard_b64encode(sign_data_bin))
# print "sign_data = " + sign_data
try:
ret = crypto.verify(x509, sign_data_bin, "xxxx", "sha1")
print "签名验证成功"
except Exception, e:
print "签名验证失败,原文或签名可能已经被篡改!"
if __name__ == "__main__":
print "sign start..."
sign_verify()
print "sign end..."
如果要求一定是sha1算法,且必须是pkcs7格式,只能os.system调用openssl命令行了(如有其他更好方法,请大拿们留言指教!):
1.分别指定cert和key来签名:
openssl smime -sign -noattr -in msg.txt -signer msa.cer -inkey msa_pkcs8.key -outform PEM -out sign.p7b
2.或者使用pfx来签名:
先将pfx转成pem格式的,不加密:
openssl pkcs12 -in msa.pfx -out msa_nodes.pem -nodes
openssl smime -sign -noattr -in msg.txt -signer msa_nodes.pem -outform PEM -out sign.p7b
附:
查看p7信息:
openssl asn1parse -inform PEM -in sign.p7b > sign_info.txt