Python-从p7s文件中提取证书

时间:2018-11-22 17:42:57

标签: python openssl cryptography pkcs#7 smime

在Python中解码传入的电子邮件,我有一个附件“ smime.p7s”。 如果我将其写入文件,则可以使用

将其提取并查看

openssl pkcs7 -inform der -print_certs <smime.p7s

我想用Python做到这一点。有一个相反过程的示例here,即如何签名邮件。

看一下OpenSSL API documentation,似乎有一个入口点PKCS7_get0_signers

这是我正在尝试的代码片段,基于天真的对签名代码的修改。

with open(fname, 'wb') as p7sfile:
    p7sfile.write(sig)
    pkcs7 = crypto._lib.PKCS7_get0_signers(sig, None, 0)

这行不通-给予

pkcs7 = crypto._lib.PKCS7_get0_signers(sig, None, 0)
TypeError: initializer for ctype 'PKCS7 *' must be a cdata pointer, not bytes

该功能似乎需要三个参数,尽管标志可能是可选的?

This line代码(来自较早的M2Crypto库)也表明入口点需要三个参数。

当我们尝试提取证书时,我不明白为什么它需要一个“ certs.stack”作为输入参数,而且我也不知道在“ flags”中添加什么“。

我敢肯定,我需要一些特殊类型的缓冲区声明来建立调用,并检索结果(例如1中的bio_in = crypto._new_mem_buf(data)前言)。有人可以建议如何做吗?

此外-M2Crypto库与Python 3.x不兼容,因此正在寻找替代方法。

1 个答案:

答案 0 :(得分:0)

我找到了一个有用的代码段here。这会将证书从PKCS7二进制对象提取到OpenSSL.crypto.X509个对象的列表中。

可以将OpenSSL.crypto.X509对象用于转储证书内容(它具有dump_certificate方法),但是很难使用这些属性,因为它们仍然是ASN.1编码并且是C类型

一旦您有了一份证书列表,就可以将每个证书转换为{原生的cryptography Certificate对象,并且更易于使用。例如:

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