从 pem 文件加载 X509Certificate2。用作 ClientCertificates 时没有可用的凭据

Loading X509Certificate2 from pem file. Results in No credentials are available when used as a ClientCertificates

我需要向服务器发出请求。此请求需要证书才能访问服务器。

如果我加载 P12 文件并将其注册为我的 http 客户端上的证书。它工作正常。

var clientCertificate = new X509Certificate2(pathToTestCert, passToTestCert);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate );

但是,如果我使用 CreateFromPemFile 从 pem 文件加载证书。

var clientCertificate= X509Certificate2.CreateFromPemFile(purePem);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);

我在向服务器发送请求时得到。

System.IO.IOException: The read operation failed, see inner exception.
 ---> System.ComponentModel.Win32Exception (0x8009030E): No credentials are available in the security package
   at System.Net.SSPIWrapper.AcquireCredentialsHandle(ISSPIInterface secModule, String package, CredentialUse intent, SCH_CREDENTIALS* scc)
   at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(CredentialUse credUsage, SCH_CREDENTIALS* secureCredential)
   at System.Net.Security.SslStreamPal.AcquireCredentialsHandleSchCredentials(X509Certificate2 certificate, SslProtocols protocols, EncryptionPolicy policy, Boolean isServer)
   at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(SslStreamCertificateContext certificateContext, SslProtocols protocols, EncryptionPolicy policy, Boolean isServer)
   at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint)
   at System.Net.Security.SecureChannel.GenerateToken(ReadOnlySpan`1 inputBuffer, Byte[]& output)
   at System.Net.Security.SecureChannel.NextMessage(ReadOnlySpan`1 incomingBuffer)
   at System.Net.Security.SslStream.ProcessBlob(Int32 frameSize)
   at System.Net.Security.SslStream.ReceiveBlobAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Security.SslStream.ReplyOnReAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Byte[] buffer)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory`1 buffer)
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory`1 buffer)
   at System.Net.Http.HttpConnection.InitialFillAsync(Boolean async)
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken
        

请注意,如果在文本编辑器中打开,pem 文件看起来像这样。

-----BEGIN PRIVATE KEY-----
.
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
.....
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
................
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
.................
-----END CERTIFICATE-----

Windows 上的 TLS 层要求将私钥写入磁盘(以特定方式)。 PEM-based 证书加载不会这样做,只有 PFX-loading 会。

让 TLS 层满意的最简单方法是

cert = new X509Certificate2(cert.Export(X509ContentType.Pfx));

也就是说,将证书+密钥导出到 PFX,然后立即再次导入它(以 SChannel 可以找到的方式(临时)将密钥写入磁盘的副作用)。您不应该 需要费心将 PFX 加载标志更改为默认值,尽管一些复杂受限的用户可能需要使用 MachineKeySet。