7

I have an certificate which I can read using the X509Certificate2 class like this:

X509Certificate2 certificate = new X509Certificate2(@"certificate.pem");

But I also have an EC private key. This are it's file contents.

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKpAuZ/Wwp7FTSCNJ56fFM4Y/rf8ltXp3xnrooPxNc1UoAoGCCqGSM49
AwEHoUQDQgAEqiRaEw3ItPsRAqdDjJCyqxhfm8y3tVrxLBAGhPM0pVhHuqmPoQFA
zR5FA3IJZaWcopieEX5uZ4KMtDhLFu/FHw==
-----END EC PRIVATE KEY-----

How do I 'feed' this private key to the certificate and eventually to my HttpClient so that it will become usable as a client certificate?

This is the rest of my code:

X509Certificate2 certificate = new X509Certificate2(@"certificate.pem");
//certificate.PrivateKey = something;
httpClientHandler.ClientCertificates.Clear();
httpClientHandler.ClientCertificates.Add(certificate);
httpClientHandler.SslProtocols = SslProtocols.Tls12;
httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;

HttpClient httpClient = new HttpClient(httpClientHandler);
HttpResponseMessage result = httpClient.GetAsync("https://server.cryptomix.com/secure/").Result;
string str = result.Content.ReadAsStringAsync().Result;
frankhommers
  • 1,169
  • 12
  • 26
  • @canton how is this related? The OP is already using certificates. The question is about something different – Panagiotis Kanavos Feb 14 '19 at 11:50
  • @canton7 the question is *not* unclear. EC keys exist. [Check this question for example](https://stackoverflow.com/questions/45111422/ec-private-key-to-cngkey-in-c-sharp). They *are* used by certain payment services. They are not certificates – Panagiotis Kanavos Feb 14 '19 at 11:52

3 Answers3

8

I think I've got it... This uses the BouncyCastle NuGet package.

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using System.Security.Cryptography.X509Certificates;
using System;
using System.IO;

string pemKey = @"-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIKpAuZ/Wwp7FTSCNJ56fFM4Y/rf8ltXp3xnrooPxNc1UoAoGCCqGSM49
AwEHoUQDQgAEqiRaEw3ItPsRAqdDjJCyqxhfm8y3tVrxLBAGhPM0pVhHuqmPoQFA
zR5FA3IJZaWcopieEX5uZ4KMtDhLFu/FHw==
-----END EC PRIVATE KEY-----";

string pemCert = @"-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----";

var keyPair = (AsymmetricKeyParameter)new PemReader(new StringReader(pemKey)).ReadObject();
var cert = (Org.BouncyCastle.X509.X509Certificate)new PemReader(new StringReader(pemCert)).ReadObject();

var builder = new Pkcs12StoreBuilder();
builder.SetUseDerEncoding(true);
var store = builder.Build();

var certEntry = new X509CertificateEntry(cert);
store.SetCertificateEntry("", certEntry);
store.SetKeyEntry("", new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });

byte[] data;
using (var ms = new MemoryStream())
{
    store.Save(ms, Array.Empty<char>(), new SecureRandom());
    data = ms.ToArray();
}

var x509Cert = new X509Certificate2(data);

The trick seems to be to combine the cert and key together into a pkcs12 container, then feed that into X509Certificate2 in one go.

Mecanik
  • 1,539
  • 1
  • 20
  • 50
canton7
  • 37,633
  • 3
  • 64
  • 77
  • 1
    See [this answer](https://stackoverflow.com/a/44770878/5301903) to decrypt an encrypted private key. e.g.: `-----BEGIN ENCRYPTED PRIVATE KEY-----` – Hans Vonn Feb 06 '20 at 16:21
  • Your code is generating: "Unable to cast object of type 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters' to type 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair'.". As per the answer here: https://stackoverflow.com/a/62580847/6583298, Since you are only using a public key and you don't actually have a pair of keys (public & private) you can't cast it as 'AsymmetricCipherKeyPair' you should cast it as 'AsymmetricKeyParameter'. – Mecanik Jan 09 '23 at 03:42
  • 1
    @Mecanik Thanks! It's been 4 years since I wrote this, so maybe something changed in BouncyCastle. Thanks for the edit – canton7 Jan 09 '23 at 09:47
8

Combining the certificate and key with openssl, and feeding that into the x509certificate class works:

openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out cert-and-key.pfx

And then using this to obtain a class with assigned private key:

X509Certificate2 certificate = new X509Certificate2("cert-and-key.pfx", "password");

And then the code in my question works.

frankhommers
  • 1,169
  • 12
  • 26
  • The accepted answer did not work for me. It was giving error: `Could not load file or assembly 'BouncyCastle.Crypto, Version=1.8.9.0, Culture=neutral, PublicKeyToken=0e99375e54769942' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)`. This works great by converting the PEM file to PFX. – SavindraSingh Jun 04 '21 at 08:10
0

You can simply use the method X509Certificate2.CreateFromPemFile. Here is the link to the Microsoft documentation!

var httpClientHandler = new HttpClientHandler();

var cert = X509Certificate2.CreateFromPemFile(certificateFile, privateKeyFile);
httpClientHandler.ClientCertificates.Add(cert);

var httpClient = new HttpClient(httpClientHandler);
HttpResponseMessage result = httpClient.GetAsync("https://server.cryptomix.com/secure/").Result;

string str = result.Content.ReadAsStringAsync().Result;

where certificateFile and privateKeyFile are file paths to your PEM files,

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 02 '23 at 23:25
  • Does this allow for an EC private key? – frankhommers Jan 03 '23 at 10:40