我正在针对执行客户端证书身份验证的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... }
}
});
答案 0 :(得分:0)
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;
}