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

C#调用证书保护的WebService时请求发送异常排查

问题描述

需要通过C#访问以下需证书认证的API端点(浏览器中选择证书后可正常获取JSON结果):
https://s5-certservices.datafordeler.dk/EJERFORTEGNELSE/Ejerfortegnelsen/1/rest/EjerskabMedStamoplysninger?BFEnr=9685073&format=json

实现了下方C#代码,但运行时抛出异常:

An error occurred while sending the request
at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)

所用代码

internal static HttpResponseMessage CallSB2CertAuthService(string certificateBase64, string certPassword, string serviceFullAddressUri)
{
    byte[] certBytes = Convert.FromBase64String(certificateBase64);
    var cert = new X509Certificate2(certBytes, certPassword);
    return DoCallSBCertAuthService(cert, serviceFullAddressUri);
}

private static HttpResponseMessage DoCallSBCertAuthService(X509Certificate2 cert, string serviceFullAddressUri)
{
  
    HttpClientHandler handler = new HttpClientHandler();

    handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    handler.ClientCertificates.Add(cert);
    handler.UseDefaultCredentials = true;


    HttpClient httpClient = new HttpClient(handler);

    try
    {
        HttpResponseMessage response = httpClient.GetAsync("https://s5-certservices.datafordeler.dk/EJERFORTEGNELSE/Ejerfortegnelsen/1/rest/Handelsoplysning?BFEnr=4393252&format=json").Result;
        return response;
    }
    catch (Exception exception)
    {
        return new HttpResponseMessage() { StatusCode = HttpStatusCode.ExpectationFailed, Content = new StringContent(exception.Message.ToString()) };
    }
}

完整异常详情

Data: {System.Collections.ListDictionaryInternal}
HResult: -2146233088
HelpLink: null
InnerException: {"An error occurred while sending the request."}
InnerExceptions: Count = 1
Message: "One or more errors occurred. (An error occurred while sending the request.)"
Source: "System.Private.CoreLib"
StackTrace: "   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)\r\n   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)\r\n   at Core.Helpers.Security.CertUtils.DoCallSBCertAuthService(X509Certificate2 cert, String serviceFullAddressUri) in C:\Core\Helpers\Security\CertUtils.cs:line 38"
TargetSite: {Void ThrowIfExceptional(Boolean)}

请问问题出在哪里?


问题分析与解决

1. 硬编码URL与传入参数不符

代码中GetAsync使用了硬编码的URL,方法参数serviceFullAddressUri并未被使用,导致实际请求的端点和预期不一致,即使传入正确目标URL也无法生效。

2. 同步阻塞调用异步方法

使用.Result直接阻塞获取异步任务结果,易引发死锁(尤其是UI或ASP.NET环境),同时会掩盖底层异常细节,难以定位真实问题。

3. 证书加载缺失必要配置

从Base64加载证书时,未指定X509KeyStorageFlags,可能导致私钥无法正常访问,进而使证书无法用于TLS握手。

4. 不必要的身份验证配置冲突

启用UseDefaultCredentials会引入额外的身份验证逻辑,与客户端证书认证可能产生冲突,建议关闭。

修正后的代码

internal static async Task<HttpResponseMessage> CallSB2CertAuthService(string certificateBase64, string certPassword, string serviceFullAddressUri)
{
    byte[] certBytes = Convert.FromBase64String(certificateBase64);
    // 添加KeyStorageFlags确保私钥可访问
    var cert = new X509Certificate2(certBytes, certPassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
    return await DoCallSBCertAuthService(cert, serviceFullAddressUri);
}

private static async Task<HttpResponseMessage> DoCallSBCertAuthService(X509Certificate2 cert, string serviceFullAddressUri)
{
    using HttpClientHandler handler = new HttpClientHandler();
    // 仅测试环境跳过证书验证,生产环境需移除该回调
    handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
    handler.ClientCertificateOptions = ClientCertificateOption.Manual;
    handler.ClientCertificates.Add(cert);
    // 关闭默认凭据,避免身份验证冲突
    handler.UseDefaultCredentials = false;

    using HttpClient httpClient = new HttpClient(handler);
    try
    {
        // 使用传入的参数URL,替换硬编码值
        HttpResponseMessage response = await httpClient.GetAsync(serviceFullAddressUri);
        return response;
    }
    catch (Exception exception)
    {
        // 展开内部异常,便于排查真实问题
        var ex = exception.InnerException ?? exception;
        return new HttpResponseMessage(HttpStatusCode.ExpectationFailed)
        {
            Content = new StringContent($"请求失败:{ex.Message}\n堆栈信息:{ex.StackTrace}")
        };
    }
}

额外排查建议

  • 检查证书是否包含私钥:调用cert.HasPrivateKey确认,若为false则需重新导出包含私钥的证书。
  • 启用TLS日志与指定协议:在代码开头添加以下配置,捕获TLS握手细节并确保使用兼容的协议版本:
    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
    httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0"); // 模拟浏览器UA,部分API会校验该字段
    
  • 检查防火墙或代理:确认程序所在环境可正常访问目标API端点,无网络拦截。

内容的提问来源于stack exchange,提问作者Steen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 07:43:22