Search code examples
bouncycastlex509certificate2

Playing around with certificates in .net5


I am trying to learn about certificates, the need for that is from an idea I had a few days ago to simplify the ordering and delivery of SSL certificates to my collegues. So I startet to investigate "how hard can it be"?

And to large entusiasm, the entire process was "not so hard", but then I came to "generating pfx certificates", with private key...

The code is from a console test app:

Creating keys and storing it.

int keysize = 2048;
CngKeyCreationParameters ckcParams = new CngKeyCreationParameters()
{
   ExportPolicy = CngExportPolicies.AllowPlaintextExport,
   KeyCreationOptions = CngKeyCreationOptions.None,
   KeyUsage = CngKeyUsages.AllUsages,
};

ckcParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(KeySize), CngPropertyOptions.None));

CngKey myCngKey = CngKey.Create(CngAlgorithm.Rsa, KeyName, ckcParams);

byte[] privatePlainTextBlob = myCngKey.Export(CngKeyBlobFormat.Pkcs8PrivateBlob);
string privateblob_string = Convert.ToBase64String(privatePlainTextBlob); 
// Now I can save the Pkcs8PrivateBlob "somewhere"

Later I can pick up this up and create a Certificate Request and send it to a certificate service by API.

byte[] cngbytes = Convert.FromBase64String( privateblob_string );
CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
RSACng rsa = new RSACng(importedkey);

request = new CertificateRequest(
   new X500DistinguishedName(order.CertName),
   rsa,
   HashAlgorithmName.SHA512,
   RSASignaturePadding.Pkcs1);

etc....

Now I can download the issued certificate in various formats from the API.
So far so good. Now I want to create PFX file and KEY file.

Creating PFX:


// My issued certificate from provider
byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);

// pfx
var certificate = new X509Certificate2(PublicKeyStr, string.Empty, X509KeyStorageFlags.Exportable);
byte[] certificateData = certificate.Export(X509ContentType.Pfx, "password");
// Now I have the pfx, but no private key

I am obviously is not capable of solving this. I have been on a journey into Bouncy Castle, with no luck (btw: where is their documentation?).

I have noticed that .net5 has a metode that I thought (again) might solve this.
X509Certificate2.CreateFromPem(ReadOnlySpan certpem, ReadOnlySpan keypem)
But then I need to get the keypem "private key pem".

My question is simple: Have I totally misunderstod or is the any way to add the needed private key to the pfx file with the information I have stored from the CngKey?

ANY suggestions, ideas, help, tips will be very welcomed. It is simply so frustrating to be so close and just fail miserably.


Solution

  • You need to associate the public certificate with the private key before exporting it as a PFX.

    // Key
    byte[] cngbytes = Convert.FromBase64String( privateblob_string );
    CngKey importedkey = CngKey.Import(cngbytes, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider);
    RSACng rsa = new RSACng(importedkey);
    
    // Cert
    byte[] PublicKeyStr = System.Text.Encoding.ASCII.GetBytes(CER_STRING);
    var certificate = new X509Certificate2(PublicKeyStr);
    
    // Together:
    X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa);
    
    // PFX:
    byte[] pfx = certWithKey.Export(X509ContentType.Pfx, pwd);