DotNet Core:如何跨平台的客户端证书TLS身份验证?

时间:2019-05-09 12:24:15

标签: .net-core smartcard pkcs#11 hsm pkcs11interop

我正在尝试开发一个跨平台应用程序(windows / mac os x),该应用程序需要使用ClientCertificate身份验证对XML文件进行签名并在服务器上发出Web请求...

主要限制是我需要在智能卡上使用证书...

我目前正在使用dotnet core 2.1。

首先,我确实尝试使用dotnet核心X509Store,但是在MacO上,无论如何我都无法访问PrivateKey对象(强制进行XML签名。),然后我丢弃了该线索。

然后,我将pkcs11interop与特定于供应商的dll一起使用来访问智能卡,并且它对于XML签名非常有效(我将pkcs11interop调用包装在RSA对象中,然后将该对象用作SignedXml内的SigningKey,),但是没有用于ClientCertificate连接。

通常我使用这种方法连接到服务器(dotnet框架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

我想知道如果不使用第三方库就可以做到这一点...

注意:法语错误消息“消息意外或格式错误”的翻译。

谢谢。

(编辑:更好的格式)

1 个答案:

答案 0 :(得分:0)

X509Certificate2是仅用于Windows的遗留类,而Windows目前尚无广泛使用的替代品或合理的多平台替代品。

使用X509Certificate2属性的byte[]值构造CKA_VALUE对象时,您只会获得没有私钥的证书。

与“ .NET兼容实现”最接近的是,就像我在Pkcs11Interop.X509Store项目中所做的那样,实现从RSA派生的类。它可以与SignedXML类一起使用,但是您已经发现,它不适用于大多数要求X509Certificate2对象具有正确关联的PrivateKey属性的SSL类。

IMO,您有三种选择:

  1. 切换到适当的.NET Core版本,并使用CopyWithPrivateKey extension method创建与X509Certificate2实现相关的RSA对象,正如@FilipNavara在其评论中指出的那样。这样的X509Certificate2对象可能(或可能不会)与SSL实现一起使用。

  2. 使用PFX / PKCS#12文件,该文件包含带有受用户密码保护的私钥的SSL客户端证书。此类文件可以加载到.NET Core支持的大多数平台上与X509Certificate2属性正确关联的PrivateKey对象中。

  3. 查找或编写不需要具有关联X509Certificate2属性的PrivateKey对象的SSL客户端实现。我目前不知道有任何具有这种设计的库。