Python 3 - 从X509证书中提取公钥并使用它加密

4 投票
1 回答
6101 浏览
提问于 2025-04-18 05:32

因为M2Crypto这个库在Python 3中不可用,所以我想找个办法来读取一个X509证书,从中提取公钥,然后用它进行RSA加密。

我现在有以下两个函数:

from ssl import PEM_cert_to_DER_cert  # standard library
from Crypto.Util import asn1  # http://pycrypto.org
from OpenSSL.crypto import *  # https://pythonhosted.org/pyOpenSSL/

def extract_publickey_1(certstr):
    """ from http://stackoverflow.com/questions/12911373 """
    der = PEM_cert_to_DER_cert(certstr)
    cert = asn1.DerSequence()
    cert.decode(der)
    tbs = asn1.DerSequence()
    tbs.decode(cert[0])
    return tbs[6]

def extract_publickey_2(certstr):
    return dump_privatekey(FILETYPE_ASN1, 
                           load_certificate(FILETYPE_PEM, certstr).get_pubkey())

第一个函数在处理某些证书时会出现IndexError错误,特别是那些不是通过命令行的OpenSSL生成的证书,而是通过一些加密库生成的(我测试过Python和C#的库)。对于通过命令行OpenSSL生成的证书,这个函数是可以正常工作的。

我检查了第二个函数的输出,发现它和第一个函数的输出不完全相同,但最后的266个字节是相等的:

extract_publickey_1(certstr)[-266:] == extract_publickey_2(certstr)[-266:]

返回True

我的问题是,这到底是怎么回事?有没有解决办法?

1 个回答

4

首先,你得明白,X.509证书是用ASN.1格式编码的,这意味着它里面包含了很多层次和各种不同的值。这种层次结构和数据类型可能是随意的,即使在DER模式下也是如此。所以,期待某个字段在固定的位置和固定的形式出现是很天真的,特别是当涉及到文本字符串时;更不用说使用“魔法常量”总是个坏主意。因此,你应该尽量使用专门的函数,比如get_pubkey()而不是像你用asn1类那样自己去解析复杂的文档

其次,你要理解,由于X.509规范与算法无关,所以并没有“RSA模数”、“RSA公钥指数”等特定字段。相反,只有一个通用的“公钥”字段,里面有一组嵌套的子字段——这些子字段指定了算法的OID和它的数值属性。例如,RSA公钥有两个属性:模数n和加密指数e;另外,你的extract_publickey_2()函数前面还加了一个总是为零的属性,我也不知道它代表什么。很有可能你的RSA实现需要的是数字参数,而不是字节数组或复杂的ASN.1值,所以你可能需要用asn1.DerSequence.decode()或者更专门的RSA函数来提取它们。

撰写回答