RSA Public, Private, and PKCS #8 key parser

Apr 25, 2014

OpenSSLKey.cs is a .NET Framework 2.0 console utility
which parses either PEM or DER RSA public keys, private keys in both traditional
SSLeay
and PKCS #8 (both encrypted and unencrypted) forms. Successfully parsed RSA public or private keys are used
to create a .NET RSACryptoServiceProvider instance and optionally export to a PKCS #12 file.
These RSA key formats are commonly created using OpenSSL and Java 2.
This utility is intended to help with RSA asymmetric key interoperability between OpenSSL, .NET and Java environments.
PEM encoded keys must be in one of the following formats:

Binary DER keys are obtained by removing the header/footer lines and b64 decoding the inner content.
(Note that the SSLeay encrypted private key contains the encryption details at the PEM level and
therefore cannot be represented in binary DER format).
For the SSLeay format, the only supported encryption this utility provides is DES-EDE3-CBC.
For the PKCS #8 format, the only algorithm currently supported by this utility is PBEWithHmacSHA1AndDESede (PKCS #5, v 2.0).
No assumptions of key size for the RSA keypair are made. The utility was tested with
key sizes in the range 1024 to 16,384 bits, the maximum size RSA key supported by the
Microsoft RSA Cryptographic Service Providers. The maximum size RSA key supported by Sun's
Java 2 JCE provider implementation is only 2048 bits.
(Note that currently Java 2 (v 1.5.0_06) does not support PKCS #5 v2 algorithms, but only
PBEWithMD5AndDES (PKCS #5, v 1.5) and PBEWithSHA1AndDESede (PKCS#12) )

Usage: opensslkey.exe [V]

where the V indicates verbose output (all key details for both public and private keys are printed).
The application prompts the user for an RSA key file (which can be in either PEM or
binary DER formats). The beginning of the file is checked to determine if it is PEM encoded.
If PEM encoded, Opensslkey determines if the key is a public or private key based on the header/footer lines.
If binary DER encoded, Opensslkey sequentially tries to asn.1 parse the binary content until a match
with a supported RSA key format is found (in the order SubjectPublicKeyInfo, RSAPrivateKey, PKCS #8 unencrypted
and PKCS #8 encrypted).
For PEM RSA Private Key formats, the PEM content is checked
for the encryption specifiers "Proc-Type:" and "DEK-Info".

For PEM public keys, the key is b64 decoded and the resulting X509 SubjectPublicKeyInfo
binary key is asn.1 parsed directly to recover the modulus and exponent data which is used to
instantiate a .NET RSACryptoServiceProvider. The public XML key string is then exported and displayed.

For the PEM RSA Private Key (RSAPrivateKey format), content between the header/footer lines is checked to see if there
is encryption information. If so, the salt is extracted from the "DEK-Info" specifier. The user is prompted for the password
used to encrypt the RSA private key. Password data is acquired via keystrokes into
a .NET 2 SecureString object. This data is used to derive the 3DES key using
the SSLeay implemention of PBKD (using an interation count of 1 and the salt value).
The b64 encrypted RSA key is b64 decoded, and decrypted using
the recovered 3DES key and salt (used as the IV). Finally, the recovered RSA private key binary is
directly asn.1 parsed to recover the RSA key components, MODULUS, E, D, P, Q, DP, DQ, InverseQ.
These RSA private key components are used to instantiate an RSACryptoServiceProvider. The XML key is then
exported and displayed.
Finally, the user is prompted to export the .NET imported private key to a PKCS #12 file. If selected,
this process generates a dummy unsigned certificate to encapsulate with the RSA keypair in the PKCS #12
file. The user is prompted for a password to protect the PKCS #12 export.

The PKCS #8 unencrypted private key (PrivateKeyInfo format) is simply an asn.1 wrapper around the unencrypted
RSA private key above.
For PKCS #8 encrypted keys (EncryptedPrivateKeyInfo) format, the outer sequences are scanned for the
supported format, parsing asn.1 content sequentially to recover the salt, iteration count and IV data. Next, the
encrypted PKCS #8 octet blob is recovered, the 3DES key recovered using and .NET 2 support for
PBKDF2 in the Rfc2898DeriveBytes class and finally the blob is decrypted with EDE3CBC.
In verbose mode, the key parameters are listed. An option to export to a password protected PKCS #12 file
concludes the parsing.