1. 程式人生 > >Python: 實現pkcs7格式數字簽名方法_20170407_七俠鎮莫尛貝

Python: 實現pkcs7格式數字簽名方法_20170407_七俠鎮莫尛貝

需求:在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