我正在尝试开发一个跨平台的应用程序(Windows/mac os x),该应用程序需要使用ClientCertificate身份验证对XML文件进行签名并在服务器上发出Web请求...
主要限制是我需要在智能卡上使用证书...
我目前使用的是dotnet core 2.1。
首先,我确实尝试使用dotnet core X509Store,但是在MacOS上,我在任何情况下都无法访问PrivateKey对象(XML签名是必需的),然后我放弃了这个线索。
然后,我将 pkcs11interop 与特定于供应商的 dll 一起使用来访问智能卡,它适用于 XML 签名(我将 pkcs11interop 调用包装在 RSA 对象中,然后我将此对象用作 SignedXML 中的签名密钥),但它不适用于客户端证书连接。
通常我使用此方法连接到服务器(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 核心,它以 PlatformNotSupportedException...
cert.PrivateKey = myRSAObject; // simple
我想知道是否可以在不使用第三方库的情况下做到这一点......
注意:法语错误消息"邮件意外或格式不正确"的翻译。
谢谢。
(编辑:更好的格式)
X509Certificate2
是专为Windows设计的遗留类,目前没有广泛可用的替代方案或理智的多平台替代品。
当您使用CKA_VALUE
属性的值构造X509Certificate2
对象时byte[]
您只会获得证书而没有私钥。
最接近".NET 兼容实现"的是实现从RSA
派生的类,就像我在 Pkcs11Interop.X509Store 项目中所做的那样。它将适用于SignedXML
类,但正如您已经发现的那样,它不适用于大多数需要具有正确关联PrivateKey
属性的对象X509Certificate2
SSL 类。
IMO 您有三种选择:
-
切换到适当的 .NET Core 版本
X509Certificate2
并使用@FilipNavara在其评论中指出CopyWithPrivateKey
扩展方法创建与RSA
实现关联的对象。此类X509Certificate2
对象可能(或者更确切地说可能不)与 SSL 实现一起使用。 -
使用 PFX/PKCS#12 文件,其中包含受用户密码保护的私钥的 SSL 客户端证书。此类文件可以加载到
X509Certificate2
对象中,PrivateKey
.NET Core 支持的大多数平台上的属性正确关联。 -
查找或编写不需要具有关联
PrivateKey
属性X509Certificate2
对象的 SSL 客户端实现。我目前不知道有任何具有这种设计的库。