I have a Certificates.p12 file that I wish to convert to a certificates.pem containing an unencrypted private key in PKCS#1 format. I have previously been able to do this by running:
openssl pkcs12 -in Certificates.p12 -out certificates.pem -nodes -clcerts
The resulting certificates.pem file has a PRIVATE KEY PEM block, as expected. However, the library I'm using does not understand this PEM block, because it expects it to be a PKCS#1 private key. The ASN.1 structure of a PKCS#1 private key is defined by RFC 3447 as:
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
}
The bad private key block in my certificates.pem does not have this PKCS#1 structure! Instead, its ASN.1 structure looks like this:
$ openssl asn1parse -i -in badprivatekey.pem
0:d=0 hl=4 l=1212 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=2 l= 13 cons: SEQUENCE
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
20:d=2 hl=2 l= 0 prim: NULL
22:d=1 hl=4 l=1190 prim: OCTET STRING [HEX DUMP]:308204A...very long hex...
What is the above format? The documentation for openssl pkcs12 only vaguely says that its output is "written in PEM format." I need a stronger guarantee that the private key PEM block is in PKCS#1 format.
The strange thing is that openssl rsa understands the strange format of the "bad" private key, and can convert it to the right PKCS#1 structure with:
openssl rsa -in badprivatekey.pem -out goodprivatekey.pem
Although openssl rsa understands the input file, the tool seems unable to tell me why, i.e. what the format of the input file is.
What is the output format of openssl pkcs12? Specifically what is the format of its private key block? How do I make openssl pkcs12 output a correct PKCS#1 private key?
Wow, that library assumes any PEM ending with
PRIVATE KEYmust be PKCS1?? That is horribly wrong. There are severalxx PRIVATE KEYformats and only one of them is PKCS1, see below.Meta: I think this is a dupe but I can't find it, so answering anyway.
OpenSSL supports FOUR different PEM formats for RSA private keys:
'traditional' or 'legacy' unencrypted which is the PKCS1 format you want (https://www.rfc-editor.org/rfc/rfc8017#appendix-A.1.2) with PEM type
RSA PRIVATE KEY(NOT justPRIVATE KEY)'traditional' or 'legacy' encrypted at PEM level with OpenSSL's (SSLeay's) custom scheme which uses quite poor (and unfixable) PBKDF; this has the same PEM type
RSA PRIVATE KEYbut added headersProc-typeandDEK-infoPKCS8 standard/generic unencrypted (https://www.rfc-editor.org/rfc/rfc5208#section-5) with PEM type
PRIVATE KEY; this is a simple ASN.1 wrapper containing an identifier for the algorithm (i.e. an OID for RSA) plus an OCTET STRING containing the algorithm-dependent part which for RSA is PKCS1PKCS8 standard/generic encrypted (https://www.rfc-editor.org/rfc/rfc5208#section-6) with PEM type
ENCRYPTED PRIVATE KEY; this encrypts the PKCS8 data within the ASN.1 with an algorithm that normally defaults to at least PBKDF2-SHA1-2048 which is decentBecause PKCS8 is more flexible, and is standard (and fairly commonly used e.g. Java), and has better encryption, it is generally preferred; see the Notes section of the manpage for the PEM_read/write functions for keys and some but not all other things.
All OpenSSL functions that read a PEM private key can read any of these (given the correct password when necessary) but which they write varies depending on the function and to an extent options. As you note
pkcs12 (import)(currently) writes PKCS8, butrsa(always) writes traditional/PKCS1.Your options are:
use
rsa(as you did), or in 1.1.0pkey -traditional, to convert to traditionaluse
pkcs12in a release before 1.0.0, like 0.9.8, when it wrote traditional formats (for multiple algorithms not just RSA). Of course using an obsolete and unsupported release may expose you to flaws and even vulnerabilities that were fixed in later releases.extract the PKCS1 part out of the (unencrypted or decrypted) PKCS8. OpenSSL uses DER for keys which means the algorithm-dependent blob, which is the value of the last field in the last group, is always the contiguous last part of the data. As an example:
You can extract the PKCS1 part using
asn1parse -strparse:Or you can convert the PKCS8 body to binary and just discard the first 22+4=26 bytes (since header len hl=4 from first asn1parse above):
PS: If it's important to only read the PKCS12 once, for example to avoid retyping the password, you can use awk like
or