Allowing Untrusted Ssl Certificates with Httpclient

Allowing Untrusted SSL Certificates with HttpClient

With Windows 8.1, you can now trust invalid SSL certs. You have to either use the Windows.Web.HttpClient or if you want to use the System.Net.Http.HttpClient, you can use the message handler adapter I wrote:
http://www.nuget.org/packages/WinRtHttpClientHandler

Docs are on the GitHub:
https://github.com/onovotny/WinRtHttpClientHandler

bypass invalid SSL certificate in .net core

ServicePointManager.ServerCertificateValidationCallback isn't supported in .Net Core.

Current situation is that it will be a
a new ServerCertificateCustomValidationCallback method for the upcoming 4.1.* System.Net.Http contract (HttpClient). .NET Core team are finalizing the 4.1 contract now. You can read about this in here on github

You can try out the pre-release version of System.Net.Http 4.1 by using the sources directly here in CoreFx or on the MYGET feed:
https://dotnet.myget.org/gallery/dotnet-core

Current WinHttpHandler.ServerCertificateCustomValidationCallback definition on Github

How to allow all https regardless of validity in .NET Core HttpClient?

Use the sample below from here

var httpClientHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpClientHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new HttpClient(httpClientHandler);

Trust a self signed certificate using Httpclient

I have seen so many question regarding this I figured I write up as a complete answer and example as I can.

Note: Using WKWebView with self-sign certs, see this answer

HttpClient Implementation

Note: Using badssl.com in this example

Managed (Default)

System.Net.Http.HttpRequestException: An error occurred while sending the request --->
System.Net.WebException: Error: TrustFailure (One or more errors occurred.) --->
System.AggregateException: One or more errors occurred. --->
System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. --->
Mono.Security.Interface.Tl

The original Mono Managed provider is getting really long in the tooth and only supports TLS1.0, in terms of security & performance I would move to using the NSUrlSession implementation.

CFNetwork (iOS 6+)

Note: As this iOS version is fairly old now and I personally do not target it anymore, so I leave this blank... (unless someone really needs me to lookup my notes for it ;-)

NSUrlSession (iOS 7+)

Xamarin provides a HttpMessageHandler subclass (NSUrlSessionHandler) that is based upon iOS' NSUrlSession.

Using it by itself against a self-signed cert will result in:

System.Net.WebException: An SSL error has occurred and a secure connection to the server cannot be made. --->
Foundation.NSErrorException:
Exception of type 'Foundation.NSErrorException' was thrown.

The problem is that a self-sign cert is considered insecure and non-trusted by iOS, thus you have to apply an ATS exception to your app so iOS knows that your app is untrusted in the Info.plist.

<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>self-signed.badssl.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>

Now that iOS knows that your app is making untrusted calls, a HttpClient request will now result in this error:

System.Net.WebException: The certificate for this server is invalid. You might be connecting to a server that is pretending to be‚ self-signed.badssl.com‚ which could put your confidential information at risk. --->
Foundation.NSErrorException: Exception of type 'Foundation.NSErrorException' was thrown.

This error is due to the fact that even though the ATS exception has been allow, the default NSUrlSession provided by iOS will apply its standard NSUrlAuthenticationChallenge to the certificate and fail since a self-signed cert can never be truly authenticated (even via client pinning) since it does not include a root certificate authority (CA) in its chain that is trusted by iOS.

Thus you need to intercept and bypass the certificate security checking provided by iOS (Yes, a big security alert, flashing red lights, etc...)

But, you can do this via creating a NSUrlSessionDataDelegate subclass that does the bypass.

public class SelfSignedSessionDataDelegate : NSUrlSessionDataDelegate, INSUrlSessionDelegate
{
const string host = "self-signed.badssl.com";
public override void DidReceiveChallenge(NSUrlSession session, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
switch (challenge.ProtectionSpace.Host)
{
case host:
using (var cred = NSUrlCredential.FromTrust(challenge.ProtectionSpace.ServerSecTrust))
{
completionHandler.Invoke(NSUrlSessionAuthChallengeDisposition.UseCredential, cred);
}
break;
default:
completionHandler.Invoke(NSUrlSessionAuthChallengeDisposition.PerformDefaultHandling, null);
break;
}
}
}

Now you need to apply that NSUrlSessionDataDelegate to a NSUrlSession and use that new session in the creation of your NSUrlSessionHandler that will be provided in the constructor of the HttpClient.

var url = "https://self-signed.badssl.com";
using (var selfSignedDelegate = new SelfSignedSessionDataDelegate())
using (var session = NSUrlSession.FromConfiguration(NSUrlSession.SharedSession.Configuration, (INSUrlSessionDelegate)selfSignedDelegate, NSOperationQueue.MainQueue))
using (var handler = new NSUrlSessionHandler(session))
using (var httpClient = new HttpClient(handler))
using (var response = await httpClient.GetAsync(url))
using (var content = response.Content)
{
var result = await content.ReadAsStringAsync();
Console.WriteLine(result);
}

Note: Example only, normally you would create a single Delegate, NSUrlSession, HttpClient, NSUrlSessionHandler and re-use it for all your requests (i.e. Singleton pattern)

Your request now works:

<html>
<head>
<title>self-signed.badssl.com</title>
</head>
<body><div id="content"><h1 style="font-size: 12vw;">
self-signed.<br>badssl.com
</h1></div>
</body>
</html>

Note: The option to supply your own custom NSUrlSession to Xamarin's NSUrlSessionHandler is really new (Nov. 2017) and not currently in a release build (alpha, beta or stable), but of course, source is available at:

  • xamarin-macios/src/Foundation/NSUrlSessionHandler.cs

Using NSUrlSession instead of HttpClient:

You can also directly use a NSUrlSession instead of HttpClient against a self-signed cert.

var url = "https://self-signed.badssl.com";
using (var selfSignedDelegate = new SelfSignedSessionDataDelegate())
using (var session = NSUrlSession.FromConfiguration(NSUrlSession.SharedSession.Configuration, (INSUrlSessionDelegate)selfSignedDelegate, NSOperationQueue.MainQueue))
{
var request = await session.CreateDataTaskAsync(new NSUrl(url));
var cSharpString = NSString.FromData(request.Data, NSStringEncoding.UTF8).ToString();
Console.WriteLine(cSharpString);
}

Note: Example only, normally you would create a single Delegate and NSUrlSession and re-use it for all your requests, i.e. Singleton pattern

Real Solution? Use Free Secure Certificates:

IHMO, avoid self-signed certs all together, even in a development environment and use one of the free certificate services and avoid all the headaches of applying ATS exceptions, custom code to intercept/bypass iOS security, etc... and make your app web services actually secure.

I personally use Let’s Encrypt:

  • https://letsencrypt.org

Ignoring SSL certificate in Apache HttpClient 4.3

The code below works for trusting self-signed certificates. You have to use the TrustSelfSignedStrategy when creating your client:

SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
sslsf).build();

HttpGet httpGet = new HttpGet("https://some-server");
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} finally {
response.close();
}

I did not include the SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER on purpose: The point was to allow testing with self signed certificates so you don't have to acquire a proper certificate from a certification authority. You can easily create a self-signed certificate with the correct host name, so do that instead of adding the SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER flag.

How can I disable the SSL Certificate check in the .NET Core http client?

TL; DR: The problem lied not within certificate validation, but in a security group rule.

  1. As Marc Gravell kindly pointed out, the DangerousAcceptAnyServerCertificateValidator already achieves the aim of ignoring certificate validation problems.
  2. After deploying the same code in an EC2 instance in the same VPC and subnet I noticed, that the .NET Core code was making one more HTTP call than the Go code (Although I still do not understand why). The IP adress was not within the allowed IP range of outgoing traffic, thus blocking the request and causing a timeout of the HttpClient.

How to ignore SSL policy to perform a HTTPClient request?

Consider trying it like this

//Handle TLS protocols
System.Net.ServicePointManager.SecurityProtocol =
System.Net.SecurityProtocolType.Tls
| System.Net.SecurityProtocolType.Tls11
| System.Net.SecurityProtocolType.Tls12;

var client = new HttpClient();
client.BaseAddress = new Uri("https://myapi");
//...

var result = await client.PostAsync(method, httpContent);


Related Topics



Leave a reply



Submit