Python: 實現pkcs7格式數字簽名方法_20170407_七俠鎮莫尛貝
阿新 • • 發佈:2019-01-29
需求:在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