【问题标题】:How to Do EC Compression on a Public Key in Python?如何在 Python 中对公钥进行 EC 压缩?
【发布时间】:2021-07-29 08:52:13
【问题描述】:

我正在尝试找到运行 openssl ec -pubin -in example.pem -inform PEM -outform DER conv_form compressed 的 Python 等效项

使用以下公钥的示例:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk
6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==
-----END PUBLIC KEY-----

运行以下命令会得到以下输出:

echo '-----BEGIN PUBLIC KEY-----\r\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk\r\n6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==\r\n-----END PUBLIC KEY-----\r\n' | openssl ec -pubin -inform PEM -outform DER -conv_form compressed | base64

read EC key
writing EC key
MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=

现在使用相同的命令加上 -text 标志并减去 base64 编码提供更多详细信息:

echo '-----BEGIN PUBLIC KEY-----\r\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk\r\n6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==\r\n-----END PUBLIC KEY-----\r\n' | openssl ec -pubin -inform PEM -outform DER -conv_form compressed -text

read EC key
Private-Key: (256 bit)
pub:
    02:e2:af:a8:b7:ba:37:3e:e2:6a:b0:1a:27:66:78:
    b6:b2:95:4f:be:a2:e4:e8:50:a2:10:5f:46:5f:34:
    f2:61:9d
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
090*�H�*�H�="⯨��7>�j�'fx���O����P�_F_4�a�%

到目前为止,我可以执行以下操作:

import base64
import cryptography
csr_crypto = cryptography.x509.load_pem_x509_csr(csr_encoded) # csr_encoded being the CSR in PEM format that the public key is derived from
pub_key = csr_crypto.public_key()
compressed_bytes = pub_key.public_bytes(cryptography.hazmat.primitives.serialization.Encoding.X962, cryptography.hazmat.primitives.serialization.PublicFormat.CompressedPoint).hex()

这导致compressed_bytes 等于

02e2afa8b7ba373ee26ab01a276678b6b2954fbea2e4e850a2105f465f34f2619d

如果您仔细观察,它与上面的pub 中的内容相同。

这些十六进制字节最终如何转换为字符串MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=,openssl 输出为压缩公钥?

【问题讨论】:

    标签: python openssl cryptography elliptic-curve


    【解决方案1】:

    发布的第一个密钥是 X.509/SPKI 格式(PEM 编码)的公钥,其中包含 未压缩 格式 0x04 + <x> + <y> 的实际密钥。
    使用 OpenSSL 语句从中派生的密钥也是 X.509/SPKI 格式的公钥,但包含 压缩 格式的实际密钥 0x02 + <x>0x03 + <x> 偶数或奇数 @987654329 @, 分别。此格式还包含完整信息,因为对于给定曲线,可以从压缩密钥中导出未压缩密钥。
    X.509/SPKI 密钥可以用 ASN.1 解析器(除了 OpenSSL)来解析,例如在线:https://lapo.it/asn1js.


    在 Python 中,可以使用 PyCryptodome 库将具有未压缩密钥的 X.509/SPKI 密钥转换为具有压缩密钥的 X.509/SPKI 密钥,请参阅export_key()

    from base64 import b64encode
    from Crypto.PublicKey import ECC
    
    x509PemWithUncompressed = '''-----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk
    6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==
    -----END PUBLIC KEY-----'''
    
    publicKey = ECC.import_key(x509PemWithUncompressed);
    x509withCompressed = publicKey.export_key(format='DER', compress=True)
    print(b64encode(x509withCompressed).decode('utf-8')) # MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=
    

    另一方面,Cryptography 库只允许导出带有未压缩密钥的 X.509/SPKI 密钥。为此,必须使用SubjectPublicKeyInfo 指定格式。无法导出为带有压缩密钥的 X.509/SPKI 密钥。选项UncompressedPointCompressedPoint 只允许以0x04 + x + y0x02/0x03 + x 格式导出,但不能以X.509/SPKI 格式导出。

    但是有一种解决方法可以实现所需的转换。在 X.509/SPKI 格式中,实际密钥(压缩或未压缩)位于末尾。前面部分包含有关曲线、数据长度等信息。对于带有压缩密钥和曲线 P-256 的 X.509/SPKI 密钥,前面部分为:0x3039301306072a8648ce3d020106082a8648ce3d030107032200 (请注意,带有 uncompressed 密钥的 X.509 /SPKI 密钥的前缀因数据长度不同而不同),并且可以用作将 0x02/0x03 + x 格式的压缩密钥转换为 X 的前缀。 509/SPKI 格式:

    from base64 import b64encode
    from cryptography.hazmat.primitives import serialization
    
    x509PemWithUncompressed = b'''-----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk
    6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==
    -----END PUBLIC KEY-----'''
    
    publicKey = serialization.load_pem_public_key(x509PemWithUncompressed) 
    compressedKey = publicKey.public_bytes(serialization.Encoding.X962, serialization.PublicFormat.CompressedPoint)
    x509withCompressed = bytes.fromhex('3039301306072a8648ce3d020106082a8648ce3d030107032200') + compressedKey 
    print(b64encode(x509withCompressed).decode('utf-8')) # MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=
    

    【讨论】:

    • 你太棒了!!
    最近更新 更多