.net-core - DotNet Core:如何跨平台客户端证书 TLS 身份验证?
问题描述
我正在尝试开发一个跨平台应用程序(windows/mac os x),该应用程序需要签署 XML 文件并使用 ClientCertificate 身份验证在服务器上发出 Web 请求......
主要限制是我需要在智能卡上使用证书......
我目前正在使用 dotnet core 2.1。
首先,我确实尝试使用 dotnet core X509Store,但在 MacOs 上我无论如何都无法访问 PrivateKey 对象(XML 签名所必需的),然后我放弃了这条线索。
然后我使用 pkcs11interop 和特定于供应商的 dll 来访问智能卡,它适用于 XML 签名(我将 pkcs11interop 调用包装在 RSA 对象中,然后我将此对象用作 SignedXml 中的 SigningKey),但它不适用于 ClientCertificate联系。
通常我使用这种方法连接到服务器(dotnet framework 4.6.1,仅限 windows):
HttpWebRequest req = WebRequest.Create("https://sometlsserver.com/") as HttpWebRequest;
req.ClientCertificates.Add(cert);
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
using (StreamReader sr = new StreamReader(res.GetResponseStream(), new UTF8Encoding(false)))
{
return sr.ReadToEnd();
}
并生成证书:
var certAttr = session.GetAttributeValue(handle, new List<CKA>
{
CKA.CKA_VALUE,
};
var cert = new X509Certificate2(certAttr[0].GetValueAsByteArray());
当我尝试连接我的经典方法时,我有几个例外:
System.Net.WebException: The SSL connection could not be established, see inner exception. Authentication failed, see inner exception.
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> System.ComponentModel.Win32Exception: Le message reçu était inattendu ou formaté de façon incorrecte
--- End of inner exception stack trace ---
at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Security.SslState.ThrowIfExceptional()
at System.Net.Security.SslState.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__47_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
at System.Threading.Tasks.ValueTask`1.get_Result()
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at System.Net.HttpWebRequest.SendRequest()
at System.Net.HttpWebRequest.GetResponse()
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.GetResponse()
...
我认为在 TLS 握手期间所需的私钥进行的加密操作丢失了,它只是失败了......
我确实尝试在私钥属性中添加用于 XML 签名的 RSA 对象,但使用 dotnet core 它以 PlatformNotSupportedException 结尾...
cert.PrivateKey = myRSAObject; // simple
我想知道是否可以在不使用第三方库的情况下做到这一点......
注意:法语错误消息“消息意外或格式不正确”的翻译。
谢谢。
(编辑:更好的格式)
解决方案
X509Certificate2
是仅为 Windows 设计的遗留类,目前没有广泛使用的替代方案或健全的多平台替代品。
当您使用属性值构造X509Certificate2
对象时,您只会获得没有私钥的证书。byte[]
CKA_VALUE
最接近“.NET 兼容实现”的方法是实现派生自我RSA
在Pkcs11Interop.X509Store项目中所做的类。它将与SignedXML
类一起使用,但正如您已经发现的那样,它不适用于大多数需要X509Certificate2
具有正确关联PrivateKey
属性的对象的 SSL 类。
IMO 你有三个选择:
切换到适当的 .NET Core 版本并使用@FilipNavara 在他的评论中指出的扩展方法
X509Certificate2
创建与您的RSA
实现关联的对象。这样的对象可能(或者更确切地说可能不会)与 SSL 实现一起使用。CopyWithPrivateKey
X509Certificate2
使用包含受用户密码保护的私钥的 SSL 客户端证书的 PFX/PKCS#12 文件。在.NET Core 支持的大多数平台上,此类文件可以加载到
X509Certificate2
对象正确关联的属性中。PrivateKey
查找或编写不需要
X509Certificate2
具有关联PrivateKey
属性的对象的 SSL 客户端实现。我目前不知道有任何具有这种设计的图书馆。
推荐阅读
- c# - C# Process.Start 打开外部 EXE 文件的两个实例
- grafana - Grafana 重命名查询结果
- c - 在c中读取BMP文件时读取存储在头数组中的信息
- go - 有没有一种优雅的方法可以通过 cgo 将大字节切片或 io.Reader 传递给 c 而无需内存复制?
- python-3.x - 如何从 Python3 中的度数列表中找到元素的数量?
- java - 如何用流重写我的代码 - 甚至可以完成吗?
- aws-lambda - Slack Slash 命令和 API 网关集成
- c# - 使用 .net core 将存储过程的结果与返回对象映射
- terraform - 在 Azure Pipeline 上使用 Terraform 删除或添加 Azure SQL 的资源锁需要什么权限
- gradle - 我想创建一个 aab 文件