.NET Core API间客户端与服务端第三方证书认证实现报错排查
.NET Core 证书认证故障修复方案
现有代码核心问题
- 客户端重复创建
HttpClient实例,提前配置了客户端证书的HttpClientHandler未绑定到实际发请求的客户端,证书没有随请求发送 - 客户端调用
GetRawCertDataString()获取的是证书原始数据的十六进制字符串,服务端用URL解码+直接构造证书的逻辑完全不匹配,直接抛出异常 - 客户端判断证书为空的分支存在空引用错误:
HttpActionContext被赋值为null后直接操作Response属性,会直接触发空指针异常 - 服务端校验逻辑错误:将客户端证书指纹与服务端本地pfx证书指纹比对,除非客户端和服务端用的是同一张证书,否则校验永远不通过
修复代码
客户端控制器逻辑
[HttpGet] public async Task<List<WeatherForecast>> Get() { List<WeatherForecast> weatherForecastList = new List<WeatherForecast>(); X509Certificate2 clientCert = Authentication.GetClientCertificate(); if (clientCert == null) { // 修复空引用问题,直接返回403即可 return Forbid("Client Certificate Required"); } // 配置Handler后直接用该Handler创建HttpClient,不要重复实例化 HttpClientHandler requestHandler = new HttpClientHandler(); requestHandler.ClientCertificates.Add(clientCert); requestHandler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; using (var httpClient = new HttpClient(requestHandler)) { var request = new HttpRequestMessage() { RequestUri = new Uri("https://localhost:44386/ServerAPI"), Method = HttpMethod.Get, }; // 转Base64传输,避免十六进制转码问题 request.Headers.Add("X-ARR-ClientCert", Convert.ToBase64String(clientCert.GetRawCertData())); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using (var response = await httpClient.SendAsync(request)) { if (response.StatusCode == System.Net.HttpStatusCode.OK) { string apiResposne = await response.Content.ReadAsStringAsync(); weatherForecastList = JsonConvert.DeserializeObject<List<WeatherForecast>>(apiResposne); } } } return weatherForecastList; }
服务端API逻辑
[HttpGet] public IEnumerable<WeatherForecast> Getcertdata() { IHeaderDictionary headers = base.Request.Headers; X509Certificate2 clientCertificate = null; string certHeaderString = headers["X-ARR-ClientCert"]; if (!string.IsNullOrEmpty(certHeaderString)) { // 先转Base64为字节数组再构造证书 byte[] certBytes = Convert.FromBase64String(certHeaderString); clientCertificate = new X509Certificate2(certBytes); // 替换为你信任的客户端证书指纹,不要和服务端pfx比对 var trustedClientCertThumbprint = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; if (clientCertificate.Thumbprint.Equals(trustedClientCertThumbprint, StringComparison.OrdinalIgnoreCase)) { // 校验通过逻辑 } else { // 校验不通过返回403 Response.StatusCode = 403; return Array.Empty<WeatherForecast>(); } } else { Response.StatusCode = 403; return Array.Empty<WeatherForecast>(); } var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }).ToArray(); }
优化建议
- 不要每次请求都新建
HttpClient,建议通过依赖注入注入IHttpClientFactory来创建客户端,避免端口耗尽问题 - 生产环境不要禁用服务端证书校验,也就是不要写
ServerCertificateCustomValidationCallback直接返回true的逻辑 - 有条件的话直接用.NET Core内置的证书认证中间件来实现校验,不需要自己在接口逻辑里处理头解析,安全性和稳定性更高
内容的提问来源于stack exchange,提问作者mohan
相关产品推荐
相关产品推荐

