Python - 从p7s文件中提取证书

4
在Python中解码传入的电子邮件,我有一个附件"smime.p7s"。 如果我将其写入文件,那么可以使用以下命令提取并查看:openssl pkcs7 -inform der -print_certs <smime.p7s 我想在Python中执行这个操作。这里有一个示例,展示了相反的过程,即如何签署一封邮件。
查看OpenSSL API文档,有一个入口点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

这个函数似乎需要三个参数,不过也许“flags”是可选的? 这行代码(来自旧版的M2Crypto库)也表明入口点需要三个参数。
我不明白为什么在我们试图提取证书时需要一个“certs.stack”作为输入参数,而且我不知道在“flags”中要放什么。
我相当确定我需要一些特殊类型的缓冲区声明来设置调用,并检索结果(就像1中的bio_in = crypto._new_mem_buf(data)引言一样)。有人能否建议如何做到这一点?
另外——M2Crypto库与Python 3.x不兼容,因此正在寻找替代方案。

可能是 从Python的PKCS7信封中提取userCertificate 的重复问题。 - stovfl
是的,这相关。但它使用的是旧的 M2Crypto 库,不兼容 Python 3.x。我发现了下面使用 PyOpenSSL 库的代码片段,我会将其作为自答提出。 - tuck1s
1个回答

1

我在这里找到了一个有用的代码片段。它可以将PKCS7二进制对象中的证书提取出来,转换成OpenSSL.crypto.X509对象列表。

OpenSSL.crypto.X509对象适合于转储证书内容(它具有dump_certificate方法),但是属性很难使用,因为它们仍然是ASN.1编码和C类型。

一旦你获得了证书列表,每个证书都可以转换成Python本地的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

此代码中对证书的检查过于简单。如果有一个可信的证书包(例如在许多Linux中可用的文件“ca-bundle.crt”),则可以做得比这更好。这还在不断改进中,但请参见https://github.com/tuck1s/sparkySecure/blob/master/readSMIMEsig.py。 - tuck1s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接