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
".