Search code examples
pythoncryptographypkcs#7pyopensslasn1crypto

How to extract the signature from a p7s file?


I need the Python code to extract from the p7s file the signature resulting from digitally signing a document, in both situations where the payload is inside of, and external to, the p7s file. I have tried several crypto packages (PyOpenSSL, PyCripto, Cryptography, ASN1Crypto, and a few others), to no avail. I am able to extract basically everything else (certificates, payload, signature timestamp, etc.), but not the encrypted digest (the signature).


Solution

  • # Python 3.10 code to extract relevant data from a PKCS#7 signature file
    
    from datetime import datetime
    from asn1crypto import cms
    from cryptography import x509
    from cryptography.hazmat.primitives.serialization import pkcs7
    
    # these are the components we are going to extract
    payload: bytes                      # the original payload
    signature: bytes                    # the digital signature
    signature_algorithm: str            # the algorithm used to generate the signature
    signature_timestamp: datetime       # the signature's timestamp
    payload_hash: bytes                 # the payload hash
    hash_algorithm: str                 # the algorithm used to calculate the payload hash
    cert_chain: list[x509.Certificate]  # the X509 certificate chain
    
    # define the PKCS#7 signature file path here
    p7s_filepath: str = 'my_signature_file_path.p7s'
    
    # load the p7s file
    with open(p7s_filepath, 'rb') as f:
        p7s_bytes: bytes = f.read()
        f.close()
    
    # extract the certificater chain
    cert_chain = pkcs7.load_der_pkcs7_certificates(p7s_bytes)
    
    # extract the needed structures
    content_info: cms.ContentInfo = cms.ContentInfo.load(p7s_bytes)
    signed_data: cms.SignedData = content_info['content']
    signer_info: cms.SignerInfo = signed_data['signer_infos'][0]
    
    # extract the payload (None if payload is detached)
    payload = signed_data['encap_content_info']['content'].native
    
    # extract the remaining components
    signature = signer_info['signature'].native
    signature_algorithm = signer_info['signature_algorithm']['algorithm'].native
    hash_algoritmo = signer_info['digest_algorithm']['algorithm'].native
    
    signed_attrs = signer_info['signed_attrs']
    for signed_attr in signed_attrs:
        match signed_attr['type'].native:
            case 'message_digest':
                payload_hash = signed_attr['values'][0].native
            case 'signing_time':
                signature_timestamp = signed_attr['values'][0].native