Skip to content
Advertisement

Python import of ECC private key in PEM encoding fails

I’m running Python version 3.8.2 and using pycryptodome version 3.9.9 to import an ECC private key in PEM encoding for later signing some data.

The following EC private key is a sample key, and I’m using it for several cross-platform projects [e.g. Java, PHP, NodeJs] and it works without any problem (it’s a NIST P-256 / secp256r1-key) key:

ecprivatekey.pem:

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

Using this key in Python is failing:

Invalid DER encoding inside the PEM file

Using a ASN1-dumper I see:

  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
       :       }
       :     }
       :   }

Now I’m converting this PEM-file to a DER-file using OpenSSL and encode the result in Base64 for using in Python:

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

This is the result:

MDECAQEEIBTZ/y3Oj31nUejEmVj3rJdSEVjm+mlWPDWI8pF+TXAcoAoGCCqGSM49AwEH

Running my import it imports the key successfully:

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

The last step is to “reconvert” the DER encoded file to a PEM-encoded one:

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

These are the results of the conversion and the ASN1-dump:

-----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)
       :     }
       :   }

The reconverted key can get imported:

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

So my question: what is “wrong” with my EC private key so that it runs within Java / PHP / NodeJs-Crypto / WebCrypto but not in Python ? Or much better: how can I import my existing EC private key in Python without any further (external) conversion ?

This is the full source of my import test program:

from Crypto.PublicKey import ECC
import base64

print("Python import EC private keyn")

# 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)

Advertisement

Answer

The reason for the failure to import my EC private key is very simple – my EC key contained (only) the private key but not the public key. This seems to be okay for Java / PHP / NodeJs-Crypto / WebCrypto (they “derive” the public key under the hood) but not in Python.

I ran into the same problem when I tried to import my EC private key in Dart (using PointyCastle & Basics_Utils), after that I generated a complete new key pair with OpenSSL, the PKCS#8 encoded key has this structure (the additional BIT STRING at the end is the public key):

  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
       :         }
       :       }
       :     }
       :   }
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement