为在 C# HttpClient 中使用证书提供一个 EC 私钥

7

我有一个证书,可以使用X509Certificate2类进行读取,如下:

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

但我也有一个EC私钥,这是它的文件内容。

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

我应该如何将这个私钥提供给证书,最终通过我的HttpClient使其成为可用的客户端证书?
这是我的其余代码:
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;

@canton 这与什么有关系呢?OP已经在使用证书了。问题是关于其他事情的。 - Panagiotis Kanavos
@canton7 这个问题并不含糊。EC密钥是存在的。例如,可以查看这个问题。它们被某些支付服务使用。它们不是证书。 - Panagiotis Kanavos
3个回答

9
我想我明白了... 这需要使用BouncyCastle NuGet包。
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);

似乎关键在于将证书和密钥合并到一个pkcs12容器中,然后一次性将其输入到X509Certificate2中。

1
请参考此答案来解密加密的私钥。例如:-----BEGIN ENCRYPTED PRIVATE KEY----- - Hans Vonn
您的代码正在生成以下内容:"无法将类型为'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters'的对象强制转换为类型'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair'。" 根据此处的答案:https://dev59.com/cGgu5IYBdhLWcg3wZmW8#62580847,由于您只使用了公钥而没有实际拥有一对密钥(公钥和私钥),因此您不能将其强制转换为'AsymmetricCipherKeyPair',而应该将其强制转换为'AsymmetricKeyParameter'。 - Mecanik
1
@Mecanik 谢谢!我写这篇文章已经4年了,也许BouncyCastle有所改变。感谢您的编辑。 - canton7

8
使用openssl将证书和密钥组合,并将其提供给x509certificate类即可。
openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out cert-and-key.pfx

然后使用此方法获取带有指定私钥的类:

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

然后我提出的代码就可以工作了。


被接受的答案对我没有用。它一直报错:无法加载文件或程序集“BouncyCastle.Crypto,Version=1.8.9.0,Culture=neutral,PublicKeyToken=0e99375e54769942”或其某个依赖项。所定位的程序集清单定义与程序集引用不匹配。(来自 HRESULT 的异常:0x80131040)。通过将PEM文件转换为PFX,这个方法非常有效。 - SavindraSingh

0

您可以直接使用方法 X509Certificate2.CreateFromPemFile。这里是Microsoft文档的链接!

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;

其中certificateFile和privateKeyFile是您PEM文件的文件路径,


1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community
这是否允许使用EC私钥? - frankhommers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接