ssl - 如何将中间证书(除了叶证书)从http客户端发送到.net(核心)5中的服务器?
问题描述
我无法在.net 5中制作 http 客户端代码以将中间证书和叶证书(在 3 个证书层次结构中)发送到服务器。但是我能够成功地将叶子证书从客户端发送到服务器。这是我的设置:
我的 Windows 盒子上有 3 个证书:
- 测试根.pem
- 测试中间体.pem
- TestLeaf.pem(没有服务器的私钥 - windows 框)
- TestLeaf.pfx(带有客户端的私钥 - windows 框)
以上证书均未添加到 Windows 证书管理器中,因为我希望最终能够在非 Windows 机器上运行相同的代码。对于我的测试,我在同一个 Windows 框中运行以下客户端和服务器代码。
在我的 windows 框中,我使用 .net 5 使用以下简单的客户端代码:
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
X509Certificate2 leafCert = new X509Certificate2(File.ReadAllBytes(@"C:\Temp\TestLeaf.pfx"), "<password>");
handler.ClientCertificates.Add(leafCert);
HttpClient httpClient = new HttpClient(handler);
StringContent content = new StringContent("{}"); //Test json string
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Application.Json);
//With local.TestServer.com resolving to localhost in the host file
HttpResponseMessage response = httpClient.PostAsync("https://local.TestServer.com/...", content).Result;
if (response.IsSuccessStatusCode)
{
var responseString = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseString);
}
else
{
Console.WriteLine(x.StatusCode);
Console.WriteLine(x.ReasonPhrase);
}
在同一个窗口框中,我有以下在 .net 5 中使用红隼的服务器端代码示例片段:
services.Configure<KestrelServerOptions>(options =>
{
// Keep track of what certs belong to each port
var certsGroupedByPort = ...;
var certsPerDistinctSslPortMap = ...;
// Listen to each distinct ssl port a cert specifies
foreach (var certsPerDistinctSslPort in certsPerDistinctSslPortMap)
{
options.Listen(IPAddress.Any, certsPerDistinctSslPort.Key, listenOptions =>
{
var httpsConnectionAdapterOptions = new HttpsConnectionAdapterOptions();
httpsConnectionAdapterOptions.ClientCertificateValidation = (clientCertificate, chain, sslPolicyErrors) =>
{
bool trusted = false;
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors)
{
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
X509Certificate2 certRoot = new X509Certificate2(@"C:\Temp\TestRoot.pem");
X509Certificate2 certIntermdiate = new X509Certificate2(@"C:\Temp\TestIntermediate.pem");
chain.ChainPolicy.CustomTrustStore.Add(certRoot);
chain.ChainPolicy.ExtraStore.Add(certIntermdiate);
trusted = chain.Build(clientCertificate);
}
return trusted;
};
httpsConnectionAdapterOptions.ServerCertificateSelector = (connectionContext, sniName) =>
{
var defaultCert = //Get default cert
return defaultCert;
};
httpsConnectionAdapterOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
httpsConnectionAdapterOptions.SslProtocols = SslProtocols.Tls12;
listenOptions.UseHttps(httpsConnectionAdapterOptions);
});
}
options.Listen(IPAddress.Any, listeningPort);
});
上面的代码按预期工作,因为客户端代码将叶子证书发送到服务器,并且服务器代码可以访问中间证书和根证书。服务器代码可以使用接收到的叶证书及其为叶证书配置的中间证书和根证书成功地重建证书层次结构。
我以下尝试将中间证书(连同叶证书)发送到服务器(以便它只能在请求中使用根证书和传入的叶证书和中间证书来构建证书层次结构)失败。
尝试通过在我的客户端代码中执行以下操作来添加中间证书:
X509Certificate2 leafCert = new X509Certificate2(File.ReadAllBytes(@"C:\Temp\TestLeaf.pfx"), ""); X509Certificate2(Convert.FromBase64String(File.ReadAllText(@"C:\Temp\TestIntermediate.pem"));
handler.ClientCertificates.Add(leafCert); handler.ClientCertificates.Add(intermediateCert);
这没有将中间证书发送到服务器。我在服务器端使用 httpsConnectionAdapterOptions.ClientCertificateValidation 的代码块验证了这一点。
问题: 有没有办法确保客户端(除了叶子证书)将中间证书发送到服务器?