19

Is there any way to convert an ECC private key to RSA PKCS#1 format? I have tried converting it to PKCS#8 first using OpenSSL:

openssl pkcs8 -topk8 -nocrypt -in EC_key.pem -out pkcs8_key.pem

This results in a pem file that is in (what i am assuming) the right PKCS8 format

-----BEGIN PRIVATE KEY-----
[snip]
-----END PRIVATE KEY-----

When trying to then convert it from PKCS#8 to PKCS#1 using the following command:

openssl pkcs8 -inform pem -nocrypt -in pkcs8_key.pem -out pkcs1_key.pem

I get the same file as from the previous step.

When using the following command for conversion:

openssl rsa –in pkcs8_key.pem –out pkcs1_key.pem

I get the following error:

47049676604576:error:0607907F:digital envelope routines:EVP_PKEY_get1_RSA:expecting an rsa key:p_lib.c:279:

Can EC keys be converted to RSA PKCS#1 keys? And if yes, how?

StackzOfZtuff
  • 17,783
  • 1
  • 50
  • 86
Sid Said
  • 193
  • 1
  • 1
  • 5

3 Answers3

40

There might be a bit of confusion here between "RSA Laboratories", the organization that edits the PKCS standards, and RSA, the cryptographic algorithm. PKCS#1 is one of the PKCS standards, thus edited by RSA Laboratories; it talks about the algorithm RSA, and only about the RSA algorithm.

In particular, there is no such thing as a "PKCS#1 format" for elliptic curve (EC) keys, because EC keys are not RSA keys -- they are EC keys, which is not at all the same kind of object.


However, confusion has spread a lot further, so let's unravel a few layers.

PKCS#1 talks about RSA and defines an ASN.1-based encoding for RSA private keys. It looks like this:

  RSAPrivateKey ::= SEQUENCE {
      version           Version,
      modulus           INTEGER,  -- n
      publicExponent    INTEGER,  -- e
      privateExponent   INTEGER,  -- d
      prime1            INTEGER,  -- p
      prime2            INTEGER,  -- q
      exponent1         INTEGER,  -- d mod (p-1)
      exponent2         INTEGER,  -- d mod (q-1)
      coefficient       INTEGER,  -- (inverse of q) mod p
      otherPrimeInfos   OtherPrimeInfos OPTIONAL
  }

We recognize here the various mathematical elements that constitute a RSA public/private key pair. Being based on ASN.1, this kind of object encodes (through DER) into some bytes. OpenSSL can produce and consume such a sequence of bytes; however, it is commonplace to further reencode these bytes into the traditional (and poorly specified) PEM format: the bytes are encoded with Base64, and a header and footer are added, that specify the kind of encoded object.

It is important to notice that the raw ASN.1-based format for RSA private keys, defined in PKCS#1, results in sequences of bytes that do NOT include an unambiguous identification for the key type. Any application that reads a DER-encoded RSA private key in that format must already know, beforehand, that it should expect a RSA private key. The PEM header, that says "RSA PRIVATE KEY", provides that information.

Since the PKCS standards don't talk about PEM, they provide their own solution to the issue of identifying the key type; it is called PKCS#8. A key in PKCS#8 format is again ASN.1-based, with a structure that looks like this:

  PrivateKeyInfo ::= SEQUENCE {
     version Version,
     privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
     privateKey PrivateKey,
     attributes [0] Attributes OPTIONAL }

  Version ::= INTEGER {v1(0)} (v1,...)

  PrivateKey ::= OCTET STRING

What this means is that a PKCS#8 object really is a wrapper around some other format. In the case of a RSA private key, the wrapper indicates (through the privateKeyAlgorithm field) that the key is really a RSA key, and the contents of the PrivateKey field (an OCTET STRING, i.e. an arbitrary sequence of bytes) really are the DER encoding of a PKCS#1 private key.

OpenSSL, by default, won't let a PKCS#8 file live its life as a DER-encoded sequence of bytes; it will again convert it to PEM, and, this time, will add the "BEGIN PRIVATE KEY" header. Note that this header does not specify the key type, since the encoded object (turned to characters through Base64) already contains the information.

(As a further complication, PKCS#8 also defines an optional, often password-based encryption of private keys; and the traditional PEM-like format that OpenSSL implements also includes some generic support for password-based encryption; so you can have multiple combinations of wrappers that specify some kind of encryption, resulting in what can only be described as an utter mess.)

Now what does this tells us about EC keys ? EC keys are not described by PKCS#1 (that talks only about RSA). However, if there is a standard somewhere that says how an EC private key can be turned into a sequence of bytes, then:

  • that sequence of bytes could be PEM-encoded by OpenSSL with some explicit text header;
  • the same sequence of bytes could be wrapped into a PKCS#8 object.

And this is exactly what happens. The standard that defines the encoding format for EC keys is SEC 1 (nominally, the standard for EC cryptography is ANSI X9.62; however, while X9.62 reused much of SEC 1, the specification for encoding private EC keys is only in SEC 1, because X9.62 concerns itself only with the encoding of public keys). In SEC 1 (section C.4), the following is defined:

  ECPrivateKey ::= SEQUENCE {
         version       INTEGER { ecPrivkeyVer1(1) },
         privateKey    OCTET STRING,
         parameters    [0] EXPLICIT ECDomainParameters OPTIONAL,
         publicKey     [1] EXPLICIT BIT STRING OPTIONAL
  }

So an encoded private key contains the private key itself (a integer in the 1..n-1 range, where n is the curve subgroup order), optionally a description or reference to the used curve, and optionally a copy of the public key (which could otherwise be recomputed).

Let's try it. We generate with OpenSSL a new EC key pair, in the standard NIST P-256 curve (which is the curve that everybody implements and uses):

$ openssl ecparam -out ec1.pem -genkey -name prime256v1

We get this, in the ec1.pem file:

$ cat ec1.pem
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBdVHnnzZmJm+Z1HAYYOZlvnB8Dj8kVx9XBH+6UCWlGUoAoGCCqGSM49
AwEHoUQDQgAEThPp/xgEov0mKg2s0GII76VkZAcCc//3quAqzg+PuFKXgruaF7Kn
3tuQVWHBlyZX56oOstUYQh3418Z3Gb1+yw==
-----END EC PRIVATE KEY-----

The first element ("EC PARAMETERS") is redundant; it contains a reference to the used curve, but this information is also present in the second element. So let's use a text editor to remove the "EC PARAMETERS", and we keep only the "EC PRIVATE KEY" part. Now my ec1.pem file looks like this:

$ cat ec1.pem
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBdVHnnzZmJm+Z1HAYYOZlvnB8Dj8kVx9XBH+6UCWlGUoAoGCCqGSM49
AwEHoUQDQgAEThPp/xgEov0mKg2s0GII76VkZAcCc//3quAqzg+PuFKXgruaF7Kn
3tuQVWHBlyZX56oOstUYQh3418Z3Gb1+yw==
-----END EC PRIVATE KEY-----

We can use OpenSSL to decode its structure:

$ openssl asn1parse -i -in ec1.pem
    0:d=0  hl=2 l= 119 cons: SEQUENCE          
    2:d=1  hl=2 l=   1 prim:  INTEGER           :01
    5:d=1  hl=2 l=  32 prim:  OCTET STRING      [HEX DUMP]:17551E79F3666266F99D4701860E665BE707C0E3F24571F57047FBA5025A5194
   39:d=1  hl=2 l=  10 cons:  cont [ 0 ]        
   41:d=2  hl=2 l=   8 prim:   OBJECT            :prime256v1
   51:d=1  hl=2 l=  68 cons:  cont [ 1 ]        
   53:d=2  hl=2 l=  66 prim:   BIT STRING

We recognize the expected ASN.1 structure, as defined by SEC 1: a SEQUENCE that contains an INTEGER of value 1 (the version field), an OCTET STRING (the privateKey itself, which is a big-endian unsigned encoding of the mathematical private key), a reference (tagged with [0]) to the used curve (in the ASN.1 object it is the OID 1.2.840.10045.3.1.7; OpenSSL translates that to the name "prime256v1"), and (tagged with [1]) a copy of the public key.

We can convert that to the (unencrypted) PKCS#8 format:

$ openssl pkcs8 -topk8 -nocrypt -in ec1.pem -out ec2.pem

which yields this:

$ cat ec2.pem
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgF1UeefNmYmb5nUcB
hg5mW+cHwOPyRXH1cEf7pQJaUZShRANCAAROE+n/GASi/SYqDazQYgjvpWRkBwJz
//eq4CrOD4+4UpeCu5oXsqfe25BVYcGXJlfnqg6y1RhCHfjXxncZvX7L
-----END PRIVATE KEY-----

that we can decode with OpenSSL:

$ openssl asn1parse -i -in ec2.pem
    0:d=0  hl=3 l= 135 cons: SEQUENCE          
    3:d=1  hl=2 l=   1 prim:  INTEGER           :00
    6:d=1  hl=2 l=  19 cons:  SEQUENCE          
    8:d=2  hl=2 l=   7 prim:   OBJECT            :id-ecPublicKey
   17:d=2  hl=2 l=   8 prim:   OBJECT            :prime256v1
   27:d=1  hl=2 l= 109 prim:  OCTET STRING      [HEX DUMP]:306B0201010420(...)

(I have truncated the hexadecimal dump.) This structure is indeed a PKCS#8 object:

  • The algorithm identifier field says: "this contains an EC key" (technically, it uses an identifier whose name is "id-ecPublicKey", but since this occurs in a PKCS#8 file everybody knows that this really means an EC private key).
  • The file includes as key parameters a reference to the used curve.
  • The key value is encoded into the contents of an OCTET STRING. If we further decode that OCTET STRING, we will find the EC private key encoded as specified by SEC 1 (amusingly, the reference to the curve appears to have been omitted in that case, since it is already present in the key parameters).

Conversion can be made in the other direction (from PKCS#8 to raw SEC 1 format) with:

$ openssl ec -in ec2.pem -out ec3.pem

You will then get in file ec3.pem exactly what you had in file ec1.pem: a PEM-encoded object with header "BEGIN EC PRIVATE KEY".


Summary: There is no such thing as an "EC key in PKCS#1 format": PKCS#1 is only for RSA keys, not EC keys. However, there is another format, analogous to PKCS#1 but made for EC keys, and defined in SEC 1. OpenSSL can convert that format into the generic PKCS#8 with the "openssl pkcs8" command, and back into SEC 1 format with "openssl ec".

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • 1
    Thank you for your informative answer, Thomas! I have an application that only accepts/supports RSA keys in PKCS#1 format whereas i was trying to pass it ECC keys and thought there may be a workaround for this issue. – Sid Said Mar 23 '15 at 09:18
0

Can EC keys be converted to RSA PKCS#1 keys? And if yes, how?

No. RSA has nothing to do with ECC.

Now for some crypto there is both a regular version and an EC version.
(Examples are: DH/ECDH, DSA/ECDSA.) But: There is simply no such thing as "ECRSA". There is only RSA.

StackzOfZtuff
  • 17,783
  • 1
  • 50
  • 86
  • 1
    In fact, ECRSA kind of exists. See e.g. [The Elliptic Curve version of the RSA crypto system](http://math.boisestate.edu/~liljanab/Crypto2Spring10/ECRSA.htm). However, it does not appear to be widely used, as it does not provide similar advantages than ECDLP vs. DLP. – user4982 Jun 16 '16 at 15:18
  • @user4982: huh. Cool. I didn't know that. Although I'd guess that ECRSA does not have a code point to (officially) store any such object inside the PKCS-defined file formats. (Correct me if I'm wrong.) – StackzOfZtuff Jun 16 '16 at 15:48
-1

PKCS8 private key can be converted to ECDSA PKCS1 private key using,

openssl ec –in pkcs8_key.pem –out pkcs1_key.pem
Venkat
  • 1