Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - How to Decrypt an Encrypted Private Key with Bouncy Castle

I have a private key that was generated by running:

openssl req -new -sha384 -x509 -days 63524 -subj "/C=CA/ST=State/L=City/O=Org/CN=Org-CA" -extensions v3_ca -keyout Org-CA.key -out Org-CA.pem -passout pass:pass1234

I need to use this private key and certificate to sign client CSRs. I used to use OpenSSL to do this, but I need to do this in C# instead.

I've found examples of what I want to do online, but those examples are using bouncy castle and an un-encrypted private key.

The key file looks like this:

-----BEGIN ENCRYPTED PRIVATE KEY-----
....
-----END ENCRYPTED PRIVATE KEY-----

And I'm trying to load it by doing this:

var privatekey = File.ReadAllBytes(@"Org-CA.key");
var rsaKeyParameters = PrivateKeyFactory.DecryptKey("pass1234".ToArray(), privatekey);

But thats not working. I get an error saying Wrong number of elements in sequence (Parameter 'seq')'

Is this how I'm supposed to be decrypting and loading the private key?

like image 740
Zeus82 Avatar asked Oct 27 '22 09:10

Zeus82


1 Answers

Depending on your .NET version, you may not need BouncyCastle at all. As of .NET Core 3.1 there is RSA.ImportEncryptedPkcs8PrivateKey() for DER encoded encrypted private PKCS#8 keys and as of .NET 5.0 there is even RSA.ImportFromEncryptedPem() for PEM encoded encrypted keys.


Otherwise with C#/BouncyCastle the import of an encrypted private PKCS#8 key is available e.g. with:

using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
...
string encPkcs8 = @"-----BEGIN ENCRYPTED PRIVATE KEY-----
                    ...
                    -----END ENCRYPTED PRIVATE KEY-----"; 
StringReader stringReader = new StringReader(encPkcs8);
PemReader pemReader = new PemReader(stringReader, new PasswordFinder("<your password>"));
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)pemReader.ReadObject();
...

with the following implementation of the IPasswordFinder interface:

private class PasswordFinder : IPasswordFinder
{
    private string password;
    public PasswordFinder(string pwd) => password = pwd;
    public char[] GetPassword() => password.ToCharArray();
}

If necessary, a conversion from keyParams to System.Security.Cryptography.RSAParameters is possible with:

using Org.BouncyCastle.Security;
...
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(keyParams);

which can be imported directly e.g. with RSA.ImportParameters().


Edit:
After digging through the C#/BouncyCastle source code, your way is also possible (which I didn't know before).
DecryptKey() has several overloads, one of which can handle the DER encoded encrypted key as already suspected by Maarten Bodewes in his comment. The latter can be easily generated with the Org.BouncyCastle.Utilities.IO.Pem.PemReader() class.
Note that this PemReader is different from the one in the first implementation (different namespaces), which is why I use the namespace explicitly in the following code snippet. With this approach, the RsaPrivateCrtKeyParameters instance can be generated as follows:

using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
...
StringReader stringReader = new StringReader(encPkcs8);
Org.BouncyCastle.Utilities.IO.Pem.PemReader pemReader = new Org.BouncyCastle.Utilities.IO.Pem.PemReader(stringReader);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pem = pemReader.ReadPemObject();
RsaPrivateCrtKeyParameters keyParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.DecryptKey("<your password>".ToCharArray(), pem.Content);
...

pem.Content contains the DER encoded encrypted private PKCS#8 key.

like image 123
Topaco Avatar answered Oct 29 '22 13:10

Topaco