You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HttpClient无法使用开发证书请求API的解决方法咨询

使用HttpClient通过证书请求API返回403 Forbidden的解决方法

问题场景

我通过以下命令生成了开发环境HTTPS证书:

dotnet dev-certs https -ep dev_cert.pfx -p 1234

随后配置了API的证书认证逻辑,代码如下:

public static class AuthenticationExtension
{
    public static void ConfigureAuthetication(this IServiceCollection services)
    {
        services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
            .AddCertificate(options =>
            {
                var cert = new X509Certificate2(@"D:\dev_cert.pfx", "1234");
                options.AllowedCertificateTypes = CertificateTypes.All;
                options.ChainTrustValidationMode = X509ChainTrustMode.CustomRootTrust;
                options.CustomTrustStore = new X509Certificate2Collection { cert };
                options.RevocationMode = X509RevocationMode.NoCheck;
                options.ValidateCertificateUse = false;
                options.Events = new CertificateAuthenticationEvents
                {
                    OnCertificateValidated = context =>
                    {
                        var validationService = context.HttpContext.RequestServices.GetService<ClientCertificateValidationService>();
                        if (validationService != null && validationService.ValidateCertificate(context.ClientCertificate))
                        {
                            Console.WriteLine("Success");
                            context.Success();
                        }
                        else
                        {
                            Console.WriteLine("invalid cert");
                            context.Fail("invalid cert");
                        }

                        return Task.CompletedTask;
                    },
                    OnChallenge = context =>
                    {
                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = context =>
                    {
                        Console.WriteLine("Failed");
                        return Task.CompletedTask;
                    }
                
                };
            });

        services.AddAuthorization();
    }
}

控制器方法添加了[Authorize]特性:

[HttpGet(Name = "GetWeatherForecast")]
[Authorize]
public IEnumerable<WeatherForecast> Get()
{
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}

测试时,Postman请求正常,但用HttpClient发起请求始终返回Forbidden状态码,而过时的WebRequest却能正常工作。测试代码如下:

using System.Net;
using System.Security.Cryptography.X509Certificates;


var handler = new HttpClientHandler();

X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(@"D:\dev_cert.pfx", "1234", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
handler.ClientCertificates.AddRange(certificates);

using var client = new HttpClient(handler);
client.BaseAddress = new Uri("https://localhost:7148/");


try
{
    var response = await client.GetAsync("weatherforecast/");
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine(responseContent);
    }
    else
    {
        Console.WriteLine($"Failed  {response.StatusCode}");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Failed  {ex}");
}

string host = @"https://localhost:7148/weatherforecast";

try
{

    ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
    req.AllowAutoRedirect = true;
    req.ClientCertificates = certificates;
    req.Method = "GET";

    WebResponse resp = req.GetResponse();

    Stream stream = resp.GetResponseStream();
    using (StreamReader reader = new StreamReader(stream))
    {
        string line = reader.ReadLine();
        while (line != null)
        {
            Console.WriteLine(line);
            line = reader.ReadLine();
        }
    }

    stream.Close();
}
catch (Exception e)
{
    Console.WriteLine(e);
}

Console.ReadLine();

解决方法

问题出在HttpClientHandler的两个关键配置缺失:

  1. 跳过服务器证书验证:和WebRequest中ServicePointManager.ServerCertificateValidationCallback的作用对应,需要为HttpClientHandler设置ServerCertificateCustomValidationCallback,跳过本地开发证书的信任验证。
  2. 手动指定客户端证书发送策略:HttpClientHandler默认不会主动发送手动添加的客户端证书,需要设置ClientCertificateOption.Manual明确告知处理器使用手动添加的证书。

修改后的HttpClient代码如下:

var handler = new HttpClientHandler();
// 明确使用手动添加的客户端证书
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
// 跳过服务器证书验证(仅开发环境使用)
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;

X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(@"D:\dev_cert.pfx", "1234", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
handler.ClientCertificates.AddRange(certificates);

using var client = new HttpClient(handler);
client.BaseAddress = new Uri("https://localhost:7148/");

try
{
    // 注意路径去掉末尾斜杠,避免重定向问题
    var response = await client.GetAsync("weatherforecast");
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine(responseContent);
    }
    else
    {
        Console.WriteLine($"Failed  {response.StatusCode}");
    }
}
catch (Exception ex)
{
    Console.WriteLine($"Failed  {ex}");
}

额外说明

  • ClientCertificateOption.Manual确保处理器会发送我们手动添加的客户端证书,避免证书被忽略。
  • ServerCertificateCustomValidationCallback仅建议在开发环境使用,生产环境需使用受信任的合法证书。
  • 如果证书私钥访问出现问题,可以尝试调整X509KeyStorageFlags参数,比如使用X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet。

内容的提问来源于stack exchange,提问作者Renato Ramos Nascimento

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:25:12