HttpClient 与客户端证书重用
HttpClient reused with client certificate
我在几个地方(比如 here, here or here)读到,在请求后直接处理 HttpClient 是一种不好的做法,最好在所有请求都发出后处理它,以允许重用连接。
为了尝试一下,我创建了一个 HttpClient 实例,并以这种方式添加到我的 class 中的静态字段中:
public class Test
{
private static X509Certificate2 _certificate;
private static HttpClient HttpClient { get; set; }
...
public Test()
{
...
if (HttpClient == null)
{
LoadCertificate();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
var handler = new WebRequestHandler();
handler.ClientCertificates.Add(_certificate);
HttpClient = new HttpClient(handler, false);
}
}
private void LoadCertificate()
{
using (var store = new X509Store(StoreName.My, CertificateStoreLocation))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateFriendlyName, true);
if (certificates.Count != 1)
throw new ArgumentException(
$"Cannot find a valid certificate with name {CertificateFriendlyName} in {CertificateStoreLocation}");
_certificate = certificates[0];
store.Close();
}
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
}
}
然后我使用我的实例通过此命令调用 Web 服务:
var result = await HttpClient.PostAsJsonAsync(completeUri, request);
我第一次使用 运行 代码时,一切正常,我得到了正确的响应,但是接下来的所有时间我都从服务器收到未经授权的消息,告诉我我没有使用客户端证书。
就好像在接下来的调用中,WebRequestHandler
没有被考虑在内。
您的修复应该如下所示:
handler.PreAuthenticate = true;
一旦您建立了与服务的连接,您就可以重新使用它,使用具有不同身份验证信息的不同客户端与其进行通信。这意味着,该服务需要知道每次哪个客户端发送请求,否则可能是安全漏洞 - 例如在上次连接的客户端下执行请求。这取决于您的身份验证机制,但基本上 WebRequestHandler
在第一个请求后设置标志 IsAuthenticated
,并在下一个请求时停止发送身份验证信息。 PreAuthenticate
选项强制在每次请求时发送授权信息。
我在几个地方(比如 here, here or here)读到,在请求后直接处理 HttpClient 是一种不好的做法,最好在所有请求都发出后处理它,以允许重用连接。
为了尝试一下,我创建了一个 HttpClient 实例,并以这种方式添加到我的 class 中的静态字段中:
public class Test
{
private static X509Certificate2 _certificate;
private static HttpClient HttpClient { get; set; }
...
public Test()
{
...
if (HttpClient == null)
{
LoadCertificate();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
var handler = new WebRequestHandler();
handler.ClientCertificates.Add(_certificate);
HttpClient = new HttpClient(handler, false);
}
}
private void LoadCertificate()
{
using (var store = new X509Store(StoreName.My, CertificateStoreLocation))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateFriendlyName, true);
if (certificates.Count != 1)
throw new ArgumentException(
$"Cannot find a valid certificate with name {CertificateFriendlyName} in {CertificateStoreLocation}");
_certificate = certificates[0];
store.Close();
}
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
}
}
然后我使用我的实例通过此命令调用 Web 服务:
var result = await HttpClient.PostAsJsonAsync(completeUri, request);
我第一次使用 运行 代码时,一切正常,我得到了正确的响应,但是接下来的所有时间我都从服务器收到未经授权的消息,告诉我我没有使用客户端证书。
就好像在接下来的调用中,WebRequestHandler
没有被考虑在内。
您的修复应该如下所示:
handler.PreAuthenticate = true;
一旦您建立了与服务的连接,您就可以重新使用它,使用具有不同身份验证信息的不同客户端与其进行通信。这意味着,该服务需要知道每次哪个客户端发送请求,否则可能是安全漏洞 - 例如在上次连接的客户端下执行请求。这取决于您的身份验证机制,但基本上 WebRequestHandler
在第一个请求后设置标志 IsAuthenticated
,并在下一个请求时停止发送身份验证信息。 PreAuthenticate
选项强制在每次请求时发送授权信息。