首页 > 解决方案 > 在撤销的证书上从 HttpClient 返回更详细的错误消息?

问题描述

我正在针对执行客户端证书身份验证的 Web 服务器编写代码。我用我的证书链创建了一个 WebRequestHandler,将它传递给一个 HttpClient 对象,然后在 HttpClient 上调用 PostAsync。这适用于未撤销的信任链上的有效证书。当证书被吊销(如预期的那样)并且 Exception 成员包含此聚合异常时,HttpResponseMessage 任务会出错:

发送请求时出错。

请求被中止:无法创建 SSL/TLS 安全通道。

我的问题是我需要一个更详细的错误。如果我从 Chrome 执行相同的操作(提交相同的已撤销客户端证书),我会收到此错误:

ERR_BAD_SSL_CLIENT_AUTH_CERT

从 Internet Explorer:

ERROR_INTERNET_SEC_CERT_REVOKED

我怎么能得到这样的错误?我不仅要告诉用户它不起作用,还要告诉用户为什么。浏览器得到更精确的错误这一事实似乎表明更多信息正在返回,而不仅仅是错误异常。这似乎不是因为故意混淆。

代码示例:

WebRequestHandler handler = new WebRequestHandler();

if (certCol != null)
{
    foreach (X509Certificate2 cert in certCol)
    {
        handler.ClientCertificates.Add(cert);
    }
}
else
{
    sLastErr = "Could not find client certificate to communicate. Certificate collection is NULL.";
    LogHelper.LogGenericError(
        _logger,
        sLastErr
        );
    return false;
}

_HttpClient = new HttpClient(handler);

_HttpClient.PostAsync(uriCM, reqContent).ContinueWith(requestTask => 
{
    HttpResponseMessage httpRespContent = null;
    bool bSuccess = false;
    if (requestTask.IsCompleted)
    {
        if (requestTask.Status == TaskStatus.RanToCompletion)
        {
            httpRespContent = requestTask.Result;
            bSuccess = true;
        }
        else if(requestTask.Status == TaskStatus.Faulted)
        {
            if (requestTask.Exception != null)
            {
                LogHelper.LogErrorWithAggregateException(_logger, "PostAsync call faulted.", requestTask.Exception);
                //exception messages in aggregate exception:
                //An error occurred while sending the request.
                //The request was aborted: Could not create SSL/TLS secure channel.
            }
            else
                LogHelper.LogError(_logger, "PostAsync call faulted.");
        }
        else
        {
            LogHelper.LogError(_logger, "PostAsync call failed.");
        }
    }
    else
    {
        LogHelper.LogError(_logger, "PostAsync call never completed. Communication Failure.");
    }

    if (bSuccess)
    {
        //it worked, do stuff...        }
    }
});

标签: c#ssltls1.2

解决方案


SslStream 引发 Win32 异常,如果证书被吊销,则报告证书被吊销。就像浏览器一样。似乎在较低级别击中 Schannel 或一些非托管代码并将其向上传输。如果 HttpClient 返回 TLS 错误,我会运行我的代码以尝试建立 SslStream+TcpClient 连接并报告异常。我猜 HttpClient 只是隐藏了错误细节,而 SslStream 将它暴露在一个内部异常中。解决了我的问题。

示例代码:

    public bool VerifyThatCanAuthenticateAsClient()
    {
        bool bSuccess = false;
        TcpClient tcpClient = null;
        SslStream sslStream = null;

        try
        {
            tcpClient = new TcpClient(
                _sHostname,
                _iPort
            );

            bSuccess = true;
        }
        catch(Exception ex)
        {
            //log exception
        }

        if(bSuccess)
        {
            bSuccess = false;

            try
            {
                sslStream = new SslStream(
                    tcpClient.GetStream(),
                    false,
                    new RemoteCertificateValidationCallback(ValidateServerCertificate),
                    new LocalCertificateSelectionCallback(SelectLocalCertificate),
                    EncryptionPolicy.RequireEncryption
                    );

                bSuccess = true;
            }
            catch (Exception ex)
            {
                //log exception
            }
        }

        if (bSuccess)
        {
            bSuccess = false;

            try
            {
                sslStream.AuthenticateAsClient(
                    _sHostname,
                    null,
                    System.Security.Authentication.SslProtocols.Tls12,
                    false
                    );

                bSuccess = true;
            }
            catch (Exception ex)
            {
                //log ex.message exception

                if(ex.InnerException != null)
                {
                    //log ex.innerexception.message
                    //this is what gives me the low level error that means its revoked if it is, or other specific tls error
                }
            }
        }

        if(sslStream != null)
        {
            sslStream.Dispose();
        }

        if(tcpClient != null)
        {
            tcpClient.Dispose();
        }

        return bSuccess;
    }

推荐阅读