Python: 实现pkcs7格式数字签名方法_20170407_七侠镇莫尛貝

原创
2017/04/13 13:18
阅读数 2.4K

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



展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部