This one bothered me for a while before I was able to find a good solution.
When you use .NET Core on Linux, there’s no issue with importing a PEM certificate from file or from a string
public static X509Certificate2 LoadCertificate(string certificateContent, string privkeyContent) { var cert = X509Certificate2.CreateFromPem(connection.Certificate, connection.PrivateKey); return cert; }
This enables you to get your certificate from vaults (HCP Vault, Azure Key Vault, Infisical) or environment variables provided to the process.
The issue is when you’re on Windows, this does not work if you use that certificate for a HTTP Client similar to this
public static void MakeHttpRequest() { X509Certificate2 cert = LoadCertificate("-----BEGIN...", "-----BEGIN..."); HttpClientHandler handler = new HttpClientHandler(); handler.ClientCertificates.Add(cert); HttpClient client = new HttpClient(handler); HttpResponseMessage response = client.GetAsync("https://example.com").Result; string responseContent = response.Content.ReadAsStringAsync().Result; }
Somewhere along, you’ll be met with the error “No credentials are available in the Security Package” in an inner exception.
The workaround for this solution, as described here , is to modify the certificate function as follows – to convert the private key to PKCS12 which meets the requirements of a persisted (non-ephemeral) private key on Windows for the cryptography operations.
public static X509Certificate2 LoadCertificate(string certificateContent, string privkeyContent) { var cert = X509Certificate2.CreateFromPem(connection.Certificate, connection.PrivateKey); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return cert; } var certOnly = new X509Certificate2(cert.Export(X509ContentType.Cert)); X509Certificate2 certWithKey; if (inCert.GetRSAPrivateKey() != null) { certWithKey = certOnly.CopyWithPrivateKey(cert.GetRSAPrivateKey()); } else { certWithKey = certOnly.CopyWithPrivateKey(cert.GetECDsaPrivateKey()); } return new X509Certificate2(certWithKey.Export(X509ContentType.Pkcs12)); }