pythoncryptographycontent-management-systempkcs#7asn1crypto

Creating and saving CMS / PKCS#7 objects in Python


I need to be able to generate, save and read CMS / PKCS#7 data in Python. It seems that it can be done using asn1crypto library, but I am having a hard time finding functions that would allow me to save the data to the disk (in PEM/DER format). There is a testbench at asn1crypto/tests/test_cms.py, but it only shows how to read CMS / PKCS#7 data from the files and store it into corresponding asn1crypto.cms objects. I was not able to find a manual or even a list of asn1crypto.cms functions (methods).

For now, I am able to generate all the necessary pieces such as a signature, encrypted data, symmetric key, etc, so what I need to do is to find a way to fuse them together into CMS / PKCS#7 compatible file formats. Basically, I am looking for an equivalent Python flow for the shell's openssl cms and openssl engine capabilities. A simple Python example showing how to create and save a CMS object (e.g., SignedData, EnvelopedData, etc) would go a long way.


Solution

  • So, after many days of playing around with asn1crypto and pkcs11 packages, I was able to create a signed data file. For signing I used the PIV signing slot in my Yubikey 5. Below is an excerpt from my script showing the essence of it (pardon the large code):

    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())
    

    I then verified the signature using openssl cms -verify -in signed_data.der -inform DER -CAfile rootCertificate.pem and it worked!