gpt4 book ai didi

c# - 客户端证书未添加到请求中(证书验证)

转载 作者:太空宇宙 更新时间:2023-11-03 12:44:50 24 4
gpt4 key购买 nike

我正在尝试做一个简单的 GET使用客户端证书向外部生产服务器请求。
他们已将我们的证书添加到他们的服务器,并且我已通过 Postman(Chrome 应用程序和 Windows native 应用程序)和标准浏览器成功发出请求:
Postman showing status OK

Postman 的 Chrome 应用程序版本使用 Chrome 的内置证书查找器。原生 Postman 应用程序需要一个 .crt 和一个 .key 文件,我已经 extracted from my .p12 file .

换句话说,证书在商店中成功找到,并且在从文件中使用时也有效(在 Windows native 应用程序中,表明它应该可以在 .NET 中使用)。

在 C# 中获取证书

在我的简单 C# (.NET Framework 4.5.1) 控制台应用程序中,我能够从商店(或从文件)获取证书,并成功地将其用于 encrypt and decrypt a file (我认为这意味着我可以从我的应用程序中完全访问它):

private static X509Certificate2 GetCertificate(string thumbprint)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection coll =
store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint,
validOnly: true);
X509Certificate2 certificate = coll.Count == 0 ? null : coll[0];
return certificate;
}

应用代码

我使用 HttpClient 向服务器发出请求或 HttpWebRequest :
//A global setting to enable TLS1.2 which is disabled in .NET 4.5.1 and 4.5.2 by default,
//and disable SSL3 which has been deprecated for a while.
//The server I'm connecting to uses TLS1.2
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;

X509Certificate cert = GetCertificate(thumbprint);
string url = "https://sapxi.example.com/XISOAPAdapter/MessageServlet";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ClientCertificates.Add(cert);
request.Method = WebRequestMethods.Http.Get;

WebResponse basicResponse = request.GetResponse(); //This is where the exception is thrown
string responseString = new StreamReader(basicResponse.GetResponseStream()).ReadToEnd();

异常(exception)

两者 HttpClientHttpWebRequest抛出相同的异常:

(WebException) The underlying connection was closed: An unexpected error occurred on a send.

(IOException) Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

(SocketException) An existing connection was forcibly closed by the remote host



在 Visual Studio 中跟踪请求

Enabling tracing ,我得到一个输出,其中找到了证书和私钥(我已经过滤掉了详细的消息):
System.Net Error: 0 : [29136] Can't retrieve proxy settings for Uri 'https://sapxi.example.com/XISOAPAdapter/MessageServlet'. Error code: 12180.
System.Net Information: 0 : [29136] Associating HttpWebRequest#21454193 with ServicePoint#60068066
System.Net Information: 0 : [29136] Associating Connection#3741682 with HttpWebRequest#21454193
System.Net.Sockets Information: 0 : [29136] Socket#33675143 - Created connection from 192.168.168.177:56114 to 131.165.*.*:443.
System.Net Information: 0 : [29136] Connection#3741682 - Created connection from 192.168.168.177:56114 to 131.165.*.*:443.
System.Net Information: 0 : [29136] TlsStream#43332040::.ctor(host=sapxi.example.com, #certs=1)
System.Net Information: 0 : [29136] Associating HttpWebRequest#21454193 with ConnectStream#54444047
System.Net Information: 0 : [29136] HttpWebRequest#21454193 - Request: GET /XISOAPAdapter/MessageServlet HTTP/1.1

System.Net Information: 0 : [29136] ConnectStream#54444047 - Sending headers
{
Host: sapxi.example.com
Connection: Keep-Alive
}.
System.Net Information: 0 : [29136] SecureChannel#20234383::.ctor(hostname=sapxi.example.com, #clientCertificates=1, encryptionPolicy=RequireEncryption)
System.Net Information: 0 : [29136] Enumerating security packages:
System.Net Information: 0 : [29136] Negotiate
System.Net Information: 0 : [29136] NegoExtender
System.Net Information: 0 : [29136] Kerberos
System.Net Information: 0 : [29136] NTLM
System.Net Information: 0 : [29136] TSSSP
System.Net Information: 0 : [29136] pku2u
System.Net Information: 0 : [29136] WDigest
System.Net Information: 0 : [29136] Schannel
System.Net Information: 0 : [29136] Microsoft Unified Security Protocol Provider
System.Net Information: 0 : [29136] Default TLS SSP
System.Net Information: 0 : [29136] CREDSSP
System.Net Information: 0 : [29136] SecureChannel#20234383 - Attempting to restart the session using the user-provided certificate:
*my certificate is here* (Issuer = CN=TRUST2408 OCES CA II, O=TRUST2408, C=DK)
System.Net Information: 0 : [29136] SecureChannel#20234383 - Left with 1 client certificates to choose from.
System.Net Information: 0 : [29136] SecureChannel#20234383 - Trying to find a matching certificate in the certificate store.
System.Net Information: 0 : [29136] SecureChannel#20234383 - Locating the private key for the certificate:
*my certificate is here*
System.Net Information: 0 : [29136] SecureChannel#20234383 - Certificate is of type X509Certificate2 and contains the private key.
System.Net Information: 0 : [29136] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent = Outbound, scc = System.Net.SecureCredential)
System.Net Information: 0 : [29136] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = (null), targetName = sapxi.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [29136] InitializeSecurityContext(In-Buffer length=0, Out-Buffer length=171, returned code=ContinueNeeded).
System.Net Information: 0 : [29136] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 278cca8:6d23888, targetName = sapxi.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [29136] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=ContinueNeeded).
System.Net Information: 0 : [29136] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 278cca8:6d23888, targetName = sapxi.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [29136] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=ContinueNeeded).
System.Net Information: 0 : [29136] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 278cca8:6d23888, targetName = sapxi.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [29136] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=ContinueNeeded).
System.Net Information: 0 : [29136] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 278cca8:6d23888, targetName = sapxi.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [29136] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=0, returned code=CredentialsNeeded).
System.Net Information: 0 : [29136] SecureChannel#20234383 - We have user-provided certificates. The server has specified 8 issuer(s). Looking for certificates that match any of the issuers.
System.Net Information: 0 : [29136] SecureChannel#20234383 - Selected certificate:
*my certificate is here*
System.Net Information: 0 : [29136] SecureChannel#20234383 - Left with 1 client certificates to choose from.
System.Net Information: 0 : [29136] SecureChannel#20234383 - Trying to find a matching certificate in the certificate store.
System.Net Information: 0 : [29136] SecureChannel#20234383 - Locating the private key for the certificate:
*my certificate is here*
System.Net Information: 0 : [29136] SecureChannel#20234383 - Certificate is of type X509Certificate2 and contains the private key.
System.Net Information: 0 : [29136] AcquireCredentialsHandle(package = Microsoft Unified Security Protocol Provider, intent = Outbound, scc = System.Net.SecureCredential)
System.Net Information: 0 : [29136] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 278cca8:6d23888, targetName = sapxi.example.com, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)
System.Net Information: 0 : [29136] InitializeSecurityContext(In-Buffers count=2, Out-Buffer length=349, returned code=ContinueNeeded).
System.Net.Sockets Error: 0 : [29136] Socket#33675143::UpdateStatusAfterSocketError() - ConnectionReset
System.Net.Sockets Error: 0 : [29136] Exception in Socket#33675143::Receive - An existing connection was forcibly closed by the remote host.
System.Net Error: 0 : [29136] Exception in HttpWebRequest#21454193:: - The underlying connection was closed: An unexpected error occurred on a send..

上面的部分再次重复,然后它最终抛出异常链。我已经用示例替换了服务器的真实 URL 和 IP。

来自的线路

We have user-provided certificates. The server has specified 8 issuer(s). Looking for certificates that match any of the issuers.





Certificate is of type X509Certificate2 and contains the private key.



让我认为在 HttpWebRequests 中正确找到了证书的内部运作。

我不知道这个输出出了什么问题。

使用 Wireshark 进行调试

在 Wireshark 中,我比较了 Postman 请求和我的 C# 代码,我看到的唯一区别是 Client Verify部分(包括整个证书)不是从 C# 发送的,而是通过 Postman(和浏览器)发送的。

通过 Postman 和浏览器,它是这样的:
Wireshark Postman successful request has "Certificate Verify"

通过 C#,这就是它的样子:
Wireshark C# unsuccessful request missing "Certificate Verify"

对我来说,我的应用程序似乎完全忽略了客户端证书。

当我不提供客户端证书( //request.ClientCertificates.Add(cert) )时,我在 Wireshark 中得到完全相同的输出,这似乎证实了这种怀疑。在 Visual Studio 的跟踪输出中,我只得到 Left with 0 client certificates to choose from.并且没有在商店中搜索证书或类似的东西。

还值得注意的是,Wireshark 表明 Postman 成功使用了 TLS1.2,而且我的应用程序代码也在使用 TLS1.2。

创建需要证书的本地网页

我让这个工作, setting up the IIS Express to require certificates然后调用它。
在页面上,我可以在 Request.ClientCertificates 中看到证书属性(property)。
在wireshark中,它不发送证书验证,所以有些东西仍然不同。

但是这个页面在我的本地机器上运行,使用的是 IIS Express 提示我安装的自签名证书。我还没有在具有有效证书的生产服务器上设置项目,看看它的行为是否相同。

我不确定这到底是什么意思,但我想我可以确认我没有忘记一些基本的东西,这要么是一个边缘情况,要么是 HttpWebRequest 的某些协议(protocol)。 C# 中的库处理不正确。

我还尝试过什么
  • Adding the entire certificate chain/collection to the request (据我所知,这是不必要的,因为服务器有颁发者/CA 来验证我的证书)
  • Getting the certificate from a .key and .crt file, combining it in the code
  • 使用 HttpClientSendAsync()WebRequestHandler
  • 面向 .NET Framework 4.6.1
  • 面向 .NET Framework 4.7.1
  • 仅启用 TLS 1.2 ( ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 )
  • 将服务器的证书添加到 request.ClientCertificates.Add(serverCert)
  • 使用 WinHttpCertCfg.exe授予对证书的访问权限,授予当前用户
  • 以管理员身份运行 Visual Studio
  • 打开和关闭各种设置:

  • .
    //Automatically verifying all server certificates (don't use this in production)
    ServicePointManager.ServerCertificateValidationCallback =
    (sender, certificate, chain, sslPolicyErrors) =>
    {
    return true; //This is not reached
    };
    ServicePointManager.Expect100Continue = true;
    request.AllowAutoRedirect = true;
    request.PreAuthenticate = true;
    request.KeepAlive = false;
    request.UserAgent = null;
    request.CachePolicy = new HttpRequestCachePolicy(
    HttpCacheAgeControl.MaxAge, TimeSpan.FromSeconds(0));
    //And several more that I didn't expect any effect from

    如果有帮助,他们的服务器正在运行 SAP XI ,这是拒绝我访问的应用程序。我不知道该设置是否与其他设置有很大不同,但是由于 Postman 能够成功执行请求,因此我不怀疑它有很大不同。在最坏的情况下,它只是一个仍然遵循标准的高于平均水平的安全协议(protocol)。

    我的主要想法是设置简单的 ASP 页面/API(需要客户端证书)并将其放在我们的生产服务器上。
    另一个想法是找到 HttpClient 的替代方案。 .
    或者更糟糕的是,创建我自己的,然后尝试复制我看到 Postman 所做的交易流程。
    但基本上我已经没有想法了。任何帮助表示赞赏。



    如果我必须制定一个特定的问题,我认为它是:

    如何使用 C# 中的 TLS 1.2 使用我的客户端证书向 SAP XI 服务器发出 GET 请求?

    另外,我不确定是否可以透露生产服务器的 URL 或 IP。当然,无论哪种方式,你们都无法自己连接到它,因为他们不允许您将证书添加到他们的服务器。所以这恐怕不能完全重现。我想透露服务器属于 KMD 没有坏处。

    但是,如果我可以成功连接到我自己的页面/服务并在那里看到客户端证书,那么我认为无论哪种方式我都会通过目标帖子,所以我认为这就是要走的路。

    很抱歉这个问题的长度,但通过这种方式,我提供了很多背景研究和细节,这应该有助于回答者和 future 的人们诊断一个非常相似的问题。我也尝试在我的问题中包含一些常见问题。

    当我弄清楚时,我当然会自己回答这个问题,如果这没有得到任何答案。

    提前致谢。

    最佳答案

    在研究如何将套接字数据捕获到 Wireshark 时,从我本地托管的页面中,我偶然发现了 an article saying that "Certificate Verify" isn't sent over TLS 1.2 in "newer versions of Windows" (如 Windows 10)。

    所以我将协议(protocol)更改为 TLS 1.0 并且请求通过了:

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

    使用 TLS 1.1 我得到一个异常(exception),不像那篇文章中的那个人所说的:

    (WebException) The request was aborted: Could not create SSL/TLS secure channel.



    为什么这行得通我目前没有时间去调查,因为我已经落后于调试这个问题的计划了,但对我来说这听起来像是一个错误,很像 another user claimed in another question .

    我找到了 a Microsoft article沿着这些路线说:

    This issue only occurs with servers that downgrade the TLS session in an ungraceful way (such as by sending a TCP reset when receiving a TLS protocol version that the server does not support)



    但是由于我从 TLS 1.2 开始,并且服务器明确接受 TLS 1.2(通过 Postman 和 Chrome),它必须是 TLS 1.2 协议(protocol)的一小部分,没有以相同的方式或其他方式实现。我仍然不明白 Postman native Windows 应用程序如何设法使用 TLS 1.2。

    值得注意的是,Internet Explorer 首先尝试 TLS 1.2,然后在 2 次重置后(如我的客户端),它只是降级到 TLS 1.0 并通过。对我来说,这听起来与文章中谈到的 Internet Explorer 更新非常相似:

    Wireshark showing IE resetting TLS 1.2 connection with Certificate Verify, and then downgrading to TLS 1.0

    我意识到这不是一个很好的答案(当谈到“为什么”的细节时),但至少它暗示了如果遇到类似问题可能会尝试什么。

    如果有人了解这个问题,甚至可能知道我如何支持 TLS 1.2,那么我将不胜感激。

    关于c# - 客户端证书未添加到请求中(证书验证),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49150920/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com