Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert .crt + .key files to X509Certificate2 programmatically in C#

I have a .crt certificate and a .key private key file on a Linux machine. The private key is in encrypted PKCS#8 format (BEGIN ENCRYPTED PRIVATE KEY...). I would like to import these into an X509Certificate2 object for further use. Since we're on Linux, we're using .NET Core 2.2 (we cannot migrate to 3.0 yet).

I have explored a few possible solutions, detailed below:

  1. Use openssl to convert the files to a .pfx and import that using X509Certificate2
    • I do not want to use this option since I don't want to execute shell code from within C#. I would like the solution to be completely programmatically achieved in C#.
  2. Use the C# BouncyCastle libraries to do either:
    • A conversion of both the certificate and the key to .pfx (as above), or
    • Importing the certificate and private key separately and using X509Certificate2.CopyWithPrivateKey() to combine them.
    • However, I cannot find an API for the C# version of BouncyCastle, so I'm not sure what methods I could possibly use to do this.
  3. Some other programmatic method in C# that I'm missing here

Essentially, the end goal is to obtain an X509Certificate2 object from the .crt and .key files. Any help/insight into what approach to use, or even a pointer to helpful BouncyCastle documentation, would be much appreciated. Thanks!

like image 306
thevioletsaber Avatar asked Mar 03 '23 08:03

thevioletsaber


1 Answers

This is possible, though not as friendly as it could be, in .NET Core 3.0:

private static byte[] UnPem(string pem)
{
    // This is a shortcut that assumes valid PEM
    // -----BEGIN words-----\nbase64\n-----END words-----
    const string Dashes = "-----";
    int index0 = pem.IndexOf(Dashes);
    int index1 = pem.IndexOf('\n', index0 + Dashes.Length);
    int index2 = pem.IndexOf(Dashes, index1 + 1);

    return Convert.FromBase64String(pem.Substring(index1, index2 - index1));
}

...

string keyPem = File.ReadAllText("private.key");
byte[] keyDer = UnPem(keyPem);
X509Certificate2 certWithKey;

using (X509Certificate2 certOnly = new X509Certificate2("certificate.cer"))
using (RSA rsa = RSA.Create())
{
    // For "BEGIN PRIVATE KEY"
    rsa.ImportPkcs8PrivateKey(keyDer, out _);
    certWithKey = certOnly.CopyWithPrivateKey(rsa);
}

using (certWithKey)
{
    Console.WriteLine(certWithKey.HasPrivateKey);
}

RSA private keys can be in three different formats, and you need to call the correct import for each one:

  • "BEGIN PRIVATE KEY": ImportPkcs8PrivateKey
  • "BEGIN ENCRYPTED PRIVATE KEY": ImportEncryptedPkcs8PrivateKey
  • "BEGIN RSA PRIVATE KEY": ImportRSAPrivateKey
like image 75
bartonjs Avatar answered Mar 29 '23 23:03

bartonjs