Python导入PEM编码的ECC私钥失败

Python import of ECC private key in PEM encoding fails

我是 运行 Python 版本 3.8.2 并使用 pycryptodome 版本 3.9.9 以 PEM 编码导入 ECC 私钥,以便稍后签署一些数据。

以下 EC 私钥是一个示例密钥,我将其用于多个跨平台项目[例如Java, PHP, NodeJs] 它可以正常工作(它是 NIST P-256 / secp256r1-key)密钥:

ecprivatekey.pem:

-----BEGIN EC PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAU2f8tzo99Z1HoxJlY
96yXUhFY5vppVjw1iPKRfk1wHA==
-----END EC PRIVATE KEY-----

在 Python 中使用此密钥失败:

Invalid DER encoding inside the PEM file

我看到使用 ASN1-dumper:

  0  65: SEQUENCE {
  2   1:   INTEGER 0
  5  19:   SEQUENCE {
  7   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
 16   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
       :     }
 26  39:   OCTET STRING, encapsulates {
 28  37:     SEQUENCE {
 30   1:       INTEGER 1
 33  32:       OCTET STRING
       :         14 D9 FF 2D CE 8F 7D 67 51 E8 C4 99 58 F7 AC 97
       :         52 11 58 E6 FA 69 56 3C 35 88 F2 91 7E 4D 70 1C
       :       }
       :     }
       :   }

现在我正在使用 OpenSSL 将此 PEM 文件转换为 DER 文件,并将结果编码为 Base64 以便在 Python:

中使用
openssl ec -in ecprivatekey.pem -outform DER -out ecprivatekey.der
openssl enc -base64 -in ecprivatekey.der -out ecprivatekey.der.base64

这是结果:

MDECAQEEIBTZ/y3Oj31nUejEmVj3rJdSEVjm+mlWPDWI8pF+TXAcoAoGCCqGSM49AwEH

运行 我的 import it 导入密钥成功:

EccKey(curve='NIST P-256', point_x=93061505133516819612094413624227760091937004899246228970231210633982641184160, point_y=83370390147869481338300161558578623699120044123289243047585106101294907287413, d=9431423964991629169983079041344798030398447908105071875075159616703093895196)

最后一步是将 DER 编码文件“重新转换”为 PEM 编码文件:

openssl ec -inform DER -in ecprivatekey.der -outform PEM -out ecprivatekey2.pem

这些是转换和 ASN1 转储的结果:

-----BEGIN EC PRIVATE KEY-----
MDECAQEEIBTZ/y3Oj31nUejEmVj3rJdSEVjm+mlWPDWI8pF+TXAcoAoGCCqGSM49
AwEH
-----END EC PRIVATE KEY-----

  0  49: SEQUENCE {
  2   1:   INTEGER 1
  5  32:   OCTET STRING
       :     14 D9 FF 2D CE 8F 7D 67 51 E8 C4 99 58 F7 AC 97
       :     52 11 58 E6 FA 69 56 3C 35 88 F2 91 7E 4D 70 1C
 39  10:   [0] {
 41   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
       :     }
       :   }

可以导入重新转换的密钥:

EccKey(curve='NIST P-256', point_x=93061505133516819612094413624227760091937004899246228970231210633982641184160, point_y=83370390147869481338300161558578623699120044123289243047585106101294907287413, d=9431423964991629169983079041344798030398447908105071875075159616703093895196)

所以我的问题是: 我的 EC 私钥有什么“错误”,因此它在 Java / PHP / NodeJs-Crypto / 中运行WebCrypto 但不在 Python 中?或者更好:如何在不进行任何进一步(外部)转换的情况下将现有的 EC 私钥导入 Python?

这是我导入测试程序的完整源代码:

from Crypto.PublicKey import ECC
import base64

print("Python import EC private key\n")

# trying to import the original EC private key
ecPrivateKeyPem = """-----BEGIN EC PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAU2f8tzo99Z1HoxJlY
96yXUhFY5vppVjw1iPKRfk1wHA==
-----END EC PRIVATE KEY-----
"""
try:
  ecPrivateKey = ECC.import_key(ecPrivateKeyPem)  
  print(ecPrivateKey)
except ValueError as e:
  print(e)
#error: Invalid DER encoding inside the PEM file

# import of the DER encoded EC private key runs:
ecPrivateKeyDerBase64 = """MDECAQEEIBTZ/y3Oj31nUejEmVj3rJdSEVjm+mlWPDWI8pF+TXAcoAoGCCqGSM49AwEH"""
ecPrivateKeyDer = base64.decodebytes(ecPrivateKeyDerBase64.encode("ascii"))
try:
  ecPrivateKey = ECC.import_key(ecPrivateKeyDer)
  print("\necPrivateKeyDer")  
  print(ecPrivateKey)
except ValueError as e:
  print(e)

ecPrivateKeyPem2 = """-----BEGIN EC PRIVATE KEY-----
MDECAQEEIBTZ/y3Oj31nUejEmVj3rJdSEVjm+mlWPDWI8pF+TXAcoAoGCCqGSM49
AwEH
-----END EC PRIVATE KEY-----
"""
try:
  ecPrivateKey2 = ECC.import_key(ecPrivateKeyPem2) 
  print("\necPrivateKeyPem2")  
  print(ecPrivateKey2)
except ValueError as e:
  print(e)

无法导入我的 EC 私钥的原因很简单 - 我的 EC 密钥包含(仅)私钥而不是 public 密钥。这对于 Java / PHP / NodeJs-Crypto / WebCrypto 似乎没问题(它们在后台“导出” public 密钥)但在 Python.

当我尝试在 Dart 中导入我的 EC 私钥时(使用 PointyCastle & Basics_Utils),我 运行 遇到了同样的问题,之后我用 OpenSSL 生成了一个完整的新密钥对, PKCS#8 编码的密钥具有以下结构(末尾的附加 BIT STRING 是 public 密钥):

  0 135: SEQUENCE {
  3   1:   INTEGER 0
  6  19:   SEQUENCE {
  8   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
 17   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
       :     }
 27 109:   OCTET STRING, encapsulates {
 29 107:     SEQUENCE {
 31   1:       INTEGER 1
 34  32:       OCTET STRING
       :         72 23 ED FE 0B A5 CF 0E FF 5D ED 76 60 EB BF BC
       :         B5 20 21 46 7E EE 01 A8 E5 59 26 53 40 7E 81 45
 68  68:       [1] {
 70  66:         BIT STRING
       :           04 31 91 E7 B7 50 F5 B5 D7 4B 34 69 44 1D 71 2D
       :           13 0E 4A FC 6E 50 1E 48 1A 2E 2F 88 57 CE 28 89
       :           5F 93 1E FF C3 A8 6C 58 0D 7D 85 E4 93 A4 7F 2B
       :           F7 EA 26 12 7F 99 5F 20 2E EA F5 E9 78 60 B9 E5
       :           C0
       :         }
       :       }
       :     }
       :   }