在Python中创建和保存CMS/PKCS7对象

在Python中创建和保存CMS/PKCS7对象,python,cryptography,content-management-system,pkcs#7,asn1crypto,Python,Cryptography,Content Management System,Pkcs#7,Asn1crypto,我需要能够生成、保存和读取Python中的CMS/PKCS7数据。似乎可以使用asn1crypto库来完成,但我很难找到能够以PEM/DER格式将数据保存到磁盘的函数。有一个,但它仅显示如何从文件中读取CMS/PKCS7数据并将其存储到相应的asn1crypto.CMS对象中。我找不到asn1crypto.cms函数和方法的手册甚至列表 现在,我能够生成所有必要的部分,例如签名、加密数据、对称密钥等,所以我需要做的是找到一种方法将它们融合到CMS/PKCS7兼容的文件格式中。基本上,我正在为sh

我需要能够生成、保存和读取Python中的CMS/PKCS7数据。似乎可以使用asn1crypto库来完成,但我很难找到能够以PEM/DER格式将数据保存到磁盘的函数。有一个,但它仅显示如何从文件中读取CMS/PKCS7数据并将其存储到相应的asn1crypto.CMS对象中。我找不到asn1crypto.cms函数和方法的手册甚至列表


现在,我能够生成所有必要的部分,例如签名、加密数据、对称密钥等,所以我需要做的是找到一种方法将它们融合到CMS/PKCS7兼容的文件格式中。基本上,我正在为shell的opensslcms和openssl引擎功能寻找一个等效的Python流。一个简单的Python示例演示了如何创建和保存CMS对象,例如SignedData、EnvelopedData等,这将大有帮助

因此,在使用asn1crypto和pkcs11包玩了很多天后,我能够创建一个签名数据文件。为了签名,我在我的Yubikey 5中使用了PIV签名槽。下面是我的脚本的一个摘录,展示了它的精髓。请原谅这段大代码:

from asn1crypto import cms, util, algos, x509, core, pem
import pkcs11
from pkcs11 import Attribute, ObjectClass, KeyType

data = b'Just a test'

# Creating a SignedData object from cms
sd = cms.SignedData()

# Populating some of its fields
sd['version']='v1'
sd['encap_content_info']=util.OrderedDict([
        ('content_type', 'data'),
        ('content', data)])
sd['digest_algorithms']=[ util.OrderedDict([
        ('algorithm', 'sha256'),
        ('parameters', None) ])

# Initiating my Yubikey smart card
lib = pkcs11.lib('.../onepin-opensc-pkcs11.so')
token = lib.get_token(token_label='PIV Card Holder pin (PIV_II)')
session = token.open(user_pin='123456')

# Getting the private key and certificate objects using pkcs11
privateKey = next(session.get_objects({
        Attribute.CLASS: ObjectClass.PRIVATE_KEY,
        Attribute.LABEL: "SIGN key" })
certObj = next(session.get_objects({
        Attribute.CLASS: ObjectClass.CERTIFICATE,
        Attribute.LABEL: 'Certificate for Digital Signature' })

# Getting the raw value (DER) of certificate and storing it in x509
cert = x509.Certificate.load(certObj[Attribute.VALUE])

# Adding this certificate to SignedData object
sd['certificates'] = [cert]

# Setting signer info section
signer_info = cms.SignerInfo()
signer_info['version']=cms_version
signer_info['digest_algorithm']=util.OrderedDict([
                ('algorithm', 'sha256'),
                ('parameters', None) ])
signer_info['signature_algorithm']=util.OrderedDict([
                ('algorithm', 'sha256_rsa'),
                ('parameters', None) ])

# Creating a signature using a private key object from pkcs11
signer_info['signature'] = privateKey.sign(
        data, 
        mechanism=pkcs11.mechanisms.Mechanism.SHA256_RSA_PKCS )

# Finding subject_key_identifier from certificate (asn1crypto.x509 object)
key_id = cert.key_identifier_value.native
signer_info['sid'] = cms.SignerIdentifier({
        'subject_key_identifier': key_id })

# Adding SignerInfo object to SignedData object
sd['signer_infos'] = [ signer_info ]

# Writing everything into ASN.1 object
asn1obj = cms.ContentInfo()
asn1obj['content_type'] = 'signed_data'
asn1obj['content'] = sd

# This asn1obj can be dumped to a disk using dump() method (DER format)
with open('signed_data.der','wb+') as fout:
    fout.write(asn1obj.dump())

然后,我使用opensslcms-verify-in signed_data.der-notify der-CAfile rootCertificate.pem验证了签名,它成功了

因此,在使用asn1crypto和pkcs11包玩了很多天后,我能够创建一个签名数据文件。为了签名,我在我的Yubikey 5中使用了PIV签名槽。下面是我的脚本的一个摘录,展示了它的精髓。请原谅这段大代码:

from asn1crypto import cms, util, algos, x509, core, pem
import pkcs11
from pkcs11 import Attribute, ObjectClass, KeyType

data = b'Just a test'

# Creating a SignedData object from cms
sd = cms.SignedData()

# Populating some of its fields
sd['version']='v1'
sd['encap_content_info']=util.OrderedDict([
        ('content_type', 'data'),
        ('content', data)])
sd['digest_algorithms']=[ util.OrderedDict([
        ('algorithm', 'sha256'),
        ('parameters', None) ])

# Initiating my Yubikey smart card
lib = pkcs11.lib('.../onepin-opensc-pkcs11.so')
token = lib.get_token(token_label='PIV Card Holder pin (PIV_II)')
session = token.open(user_pin='123456')

# Getting the private key and certificate objects using pkcs11
privateKey = next(session.get_objects({
        Attribute.CLASS: ObjectClass.PRIVATE_KEY,
        Attribute.LABEL: "SIGN key" })
certObj = next(session.get_objects({
        Attribute.CLASS: ObjectClass.CERTIFICATE,
        Attribute.LABEL: 'Certificate for Digital Signature' })

# Getting the raw value (DER) of certificate and storing it in x509
cert = x509.Certificate.load(certObj[Attribute.VALUE])

# Adding this certificate to SignedData object
sd['certificates'] = [cert]

# Setting signer info section
signer_info = cms.SignerInfo()
signer_info['version']=cms_version
signer_info['digest_algorithm']=util.OrderedDict([
                ('algorithm', 'sha256'),
                ('parameters', None) ])
signer_info['signature_algorithm']=util.OrderedDict([
                ('algorithm', 'sha256_rsa'),
                ('parameters', None) ])

# Creating a signature using a private key object from pkcs11
signer_info['signature'] = privateKey.sign(
        data, 
        mechanism=pkcs11.mechanisms.Mechanism.SHA256_RSA_PKCS )

# Finding subject_key_identifier from certificate (asn1crypto.x509 object)
key_id = cert.key_identifier_value.native
signer_info['sid'] = cms.SignerIdentifier({
        'subject_key_identifier': key_id })

# Adding SignerInfo object to SignedData object
sd['signer_infos'] = [ signer_info ]

# Writing everything into ASN.1 object
asn1obj = cms.ContentInfo()
asn1obj['content_type'] = 'signed_data'
asn1obj['content'] = sd

# This asn1obj can be dumped to a disk using dump() method (DER format)
with open('signed_data.der','wb+') as fout:
    fout.write(asn1obj.dump())

然后,我使用opensslcms-verify-in signed_data.der-notify der-CAfile rootCertificate.pem验证了签名,它成功了

保存到磁盘是最后一步;对于较小的消息,至少在内存中创建它们更有意义。对于更大范围的数据,您可以尝试直接流式传输到磁盘。请注意,Python等编程语言通常在高级上运行,并且可能不会在所有API调用中提供如此低级的构造。@Maarten,我对作为asn1crypto包一部分的cms.py进行了彻底的审查,我的理解是,我可以生成证书、密钥、签名、,使用pkcs11和OpenSSL进行etc,然后将它们分配给相应对象(例如SignedData)的相应字段。可能有一些方法继承自asn1类,但很难跟踪它们。你认为这是正确的方法吗?是的,看看SignedData和SignedInfo类,可能还有singer对象SignatureGenerator或类似的东西。不幸的是,我不熟悉Python API。保存到磁盘是最后一步;对于较小的消息,至少在内存中创建它们更有意义。对于更大范围的数据,您可以尝试直接流式传输到磁盘。请注意,Python等编程语言通常在高级上运行,并且可能不会在所有API调用中提供如此低级的构造。@Maarten,我对作为asn1crypto包一部分的cms.py进行了彻底的审查,我的理解是,我可以生成证书、密钥、签名、,使用pkcs11和OpenSSL进行etc,然后将它们分配给相应对象(例如SignedData)的相应字段。可能有一些方法继承自asn1类,但很难跟踪它们。你认为这是正确的方法吗?是的,看看SignedData和SignedInfo类,可能还有singer对象SignatureGenerator或类似的东西。不幸的是,我不熟悉Python API。