Decoding an incoming email in Python, I have an attachment "smime.p7s". If I write this to a file, then it can be extracted and viewed using
openssl pkcs7 -inform der -print_certs <smime.p7s
I'd like to do that in Python. There's an example here of the inverse process, i.e. how to sign a mail.
Looking at the OpenSSL API documentation there is an entry point PKCS7_get0_signers which seems to do this.
Here's the code snippet I'm trying, based on a naive reworking of the signing code.
with open(fname, 'wb') as p7sfile:
p7sfile.write(sig)
pkcs7 = crypto._lib.PKCS7_get0_signers(sig, None, 0)
It doesn't work - giving
pkcs7 = crypto._lib.PKCS7_get0_signers(sig, None, 0)
TypeError: initializer for ctype 'PKCS7 *' must be a cdata pointer, not bytes
The function seems to require three parameters, although maybe flags is optional?
This line of code (from the older M2Crypto library) also suggests that entry point needs three parameters.
I don't understand why it would need a "certs.stack" as an input param when we are trying to extract the certs, and I don't understand what to put in "flags".
I'm pretty sure I need some specially typed buffer declarations to set up the call, and also retrieve the results (like the bio_in = crypto._new_mem_buf(data) preamble in 1). Can someone please suggest how to do it?
Also - the M2Crypto library is not compatible with Python 3.x, hence looking for an alternative.
I found a useful code snippet here. This extracts certs from a PKCS7 binary object into a list of OpenSSL.crypto.X509 objects.
The OpenSSL.crypto.X509 object is OK for dumping out the certificate contents (it has a dump_certificate method), but the attributes are hard to work with as they are still ASN.1 encoded and are C types.
Once you've got a list of certs, each can be converted into a cryptography Certificate object which is Python native and more amenable. For example:
class Cert(object):
"""
Convenient container object for human-readable and output-file friendly certificate contents
"""
pem = ''
email_signer = None
startT = None
endT = None
issuer = {}
algorithm = None
def extract_smime_signature(payload):
"""
Extract public certificates from the PKCS7 binary payload
:param payload: bytes
:return: list of Cert objects
"""
pkcs7 = crypto.load_pkcs7_data(crypto.FILETYPE_ASN1, payload)
certs = get_certificates(pkcs7)
certList = []
# Collect the following info from the certificates
all_cert_times_valid = True
for c in certs:
# Convert to the modern & easier to use https://cryptography.io library objects
c2 = crypto.X509.to_cryptography(c)
c3 = Cert()
# check each certificate's time validity, ANDing cumulatively across each one
c3.startT = c2.not_valid_before
c3.endT = c2.not_valid_after
now = datetime.now()
all_cert_times_valid = all_cert_times_valid and (c3.startT <= now) and (now <= c3.endT)
# get Issuer, unpacking the ASN.1 structure into a dict
for i in c2.issuer.rdns:
for j in i:
c3.issuer[j.oid._name] = j.value
# get email address from the cert "subject" - consider more than one address in the bundle as an error
for i in c2.subject.rdns:
for j in i:
attrName = j.oid._name
if attrName == 'emailAddress':
c3.email_signer = j.value
# Get hash alg - just for interest
c3.algorithm = c2.signature_hash_algorithm.name
c3.pem = c2.public_bytes(serialization.Encoding.PEM).decode('utf8')
certList.append(c3)
return certList
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With