Description
I'm building a cross-platform auth library and I am stumped trying to detect a challenge for a client certificate (for PKI-secured HTTPS services). In the past the norm used to be to look for 401/Unauthorized replies, but these days many TLS/SSLv3-secured services use error alerts instead. This happens when the connection is still negotiated so I have no HttpResponseMessage to work with — all I have is an HttpRequestException.
On most platforms, I'm able to detect this. For example:
- With SocketsHttpHandler on iOS, an InnerException (Interop+AppleCrypto+SslException) gives me a specific libsecurity error code via HResult. I can check if it matches e.g. errSSLPeerBadCert (-9825) and prompt user for a client certificate.
- With AndroidMessageHandler, an InnerException (Javax.Net.Ssl.SSLException) gives me a detailed message from OpenSSL that includes easily testable strings like "SSLV3_ALERT_BAD_CERTIFICATE".
- Old Xamarin "Managed" handler on Android used to give me an InnerException with the string "CERTIFICATE_VERIFY_FAILED"
- Old Xamarin "Native" handler on Android used to give me an Javax.Net.Ssl.SSLHandshakeException with a detailed inner Java.Security.Cert.CertificateException
But with SocketsHttpHandler on Android, the inner exception (Interop+AndroidCrypto+SslException) the HResult is always just SecurityStatusPalErrorCode.InternalError (14). I bet there is more information available to the interop but there does not seem to be any way for me to access it.
Using LocalCertificateSelectionCallback also doesn't help because it just fires once for all HTTPS connections, with no way to know whether a client certificate is required or not.
Reproduction Steps
Use an HttpClient backed by SocketsHttpHandler to make an HTTPS request to a server that requires client certificate authentication:
var client = new HttpClient(new SocketsHttpHandler());
try
{
await client.GetAsync("https://your-pki-secured-server-here.com/");
}
catch (Exception ex)
{
// try to figure out if we were asked for client certificate authentication here
}
Unfortunately I do not have a specific public-facing service to share, but it should be possible to set one up for testing.
Expected behavior
SocketsHttpHandler throws an informative exception, from which we can determine that the server needs a client certificate. Perhaps an HttpRequestException with HttpRequestError.UserAuthenticationError would work.
Actual behavior
SocketsHttpHandler throws a generic HttpRequestException with "Unknown" HttpRequestError.
System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.IO.IOException: The read operation failed, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> Interop+AndroidCrypto+SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.
The inner-most exception has a generic HResult of 14 ("internal error").
Regression?
Regression only if you compare the behavior to the old "managed" monodroid HttpClient.
Known Workarounds
Using AndroidMessageHandler instead of SocketsHttpHandler.
Configuration
No response
Other information
No response
Description
I'm building a cross-platform auth library and I am stumped trying to detect a challenge for a client certificate (for PKI-secured HTTPS services). In the past the norm used to be to look for 401/Unauthorized replies, but these days many TLS/SSLv3-secured services use error alerts instead. This happens when the connection is still negotiated so I have no HttpResponseMessage to work with — all I have is an HttpRequestException.
On most platforms, I'm able to detect this. For example:
But with SocketsHttpHandler on Android, the inner exception (Interop+AndroidCrypto+SslException) the HResult is always just SecurityStatusPalErrorCode.InternalError (14). I bet there is more information available to the interop but there does not seem to be any way for me to access it.
Using LocalCertificateSelectionCallback also doesn't help because it just fires once for all HTTPS connections, with no way to know whether a client certificate is required or not.
Reproduction Steps
Use an HttpClient backed by SocketsHttpHandler to make an HTTPS request to a server that requires client certificate authentication:
Unfortunately I do not have a specific public-facing service to share, but it should be possible to set one up for testing.
Expected behavior
SocketsHttpHandler throws an informative exception, from which we can determine that the server needs a client certificate. Perhaps an HttpRequestException with HttpRequestError.UserAuthenticationError would work.
Actual behavior
SocketsHttpHandler throws a generic HttpRequestException with "Unknown" HttpRequestError.
The inner-most exception has a generic HResult of 14 ("internal error").
Regression?
Regression only if you compare the behavior to the old "managed" monodroid HttpClient.
Known Workarounds
Using AndroidMessageHandler instead of SocketsHttpHandler.
Configuration
No response
Other information
No response