How to detect SSL Policy errors with .net? - c#

I'm totally new to handling policy errors when making web requests so I'm a little bit confused at this point..
I have this task to call a web service but not allow the call to be made if the server I'm calling has an invalid certificate.
So I created a method to call a site with invalid cerificate and using ServerCertificateValidationCallback to prevent the call to be made if the certificate is invalid.
What I need is a quick walkthrough in how to detect the invalid certificate inside my handler. I would have thought that the call to "revoked.badssl.com" would have caused something in the sslPolicyErrors object to be something other than "none" but is this not the case? I see no difference at this point in calling badssl or my other url that has a valid certificate.
For example: if https://pinning-test.badssl.com/ is opened in chrome it shows a "ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN" (although IE shows the page). How do I find this information that chrome deems as an invalid certificate so I can, if I want to, also handle it as invalid in my code??
This is my code I'm trying with at the moment:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) =>
{
if(someError?!)
return false;
return true;
};
using (HttpClient client = DefaultHttpClient())
{
Uri uri = new Uri("https://revoked.badssl.com/");
string jsonObj = "{}";
var content = new StringContent(jsonObj, Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(uri, content).Result;
}

By default the revocation check is not performed. You need to set it on the ServicePointManger Class for your application to check it.
System.Net.ServicePointManager.CheckCertificateRevocationList = true;

Related

C# could not create ssl/tls secure channel (Restsharp)

I'm currently trying to connect to an api but I am getting this error:
could not create ssl/tls secure channel
The request works when its made through Postman, same endpoint, same certificate and everything, but when I am making the request through restsharp it stops with the SSL/TLS error
I've tried forcing the security protocol to TLS12 with the code:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Also tried to remove the certificate validation with
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
And some other variations of this code.
Tried to disable it on the client as well, with no success either
client.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
So I am currently trying to use Wireshark to get some more information. From what i could understand, it isn't an issue with the cipher suite, since it passes the "Client Hello" and "Server Hello" and it stops on the "Certificate Request, Server Hello Done".
The errors that appear on the image also appear when I make the request with Postman.
It stops on this line when it receives a RST flag with the ACK.
Does anyone have any idea on why it isn't working?
Also it's worth mentioning that I used the same code many times before to call other api's and they seem to work fine. Just this one that I can't make work no matter what I try. I don't have access to the servers since its a third-party api.
Any help on this matter is appreciated.
Edit: To add the code that i use to make the api call and some more info that was asked
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
ServicePointManager.ServerCertificateValidationCallback += (sender, certificateReturn, chainReturn, sslPolicyErrors) => true;
var client = new RestClient(api);
client.Timeout = -1;
//Add Certificate
X509Certificate2 cert = null;
if (GCONTABANCO.SelectCERTIFICADO(IDCONTABANCO, ref cert, ref MSG) == false) { return false; }
client.ClientCertificates = new X509CertificateCollection();
client.ClientCertificates.Add(cert);
var request = new RestRequest(Method.POST);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", clientId);
request.AddParameter("client_secret", clientSecret);
request.AddParameter("scope", "extrato.read boleto-cobranca.read boleto-cobranca.write");
IRestResponse response = client.Execute(request);
I'm using .net framework 4.5.2, i tried updating to 4.7 but the error persisted and currently i can't update the version because its a big system and can't risk breaking it
Windows build number: 21H1 (10.0.19043)
So i managed to fix the issue with the help from their IT architect, i'll leave the answer here in case someone runs into the same issue in the future.
Since they had a lot of certificates being sent through the connection, it was going over the limit allowed and closing the connection, so i had to change the limit to be able to connect to the server. In C# it has to be changed on the regedit.
To do so i had to:
Open regedit.
Go to: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Messaging
Then add a MessageLimitClient DWORD value.
Set the value to 65536.
Restart the machine.
After doing that and testing again the connection was successful.

Make HTTPS request using HttpClient in Xamarin.iOS

I'm trying to get the data from a WCF serving json data from a https url.
Using this code it works great pointing at a HTTP url (note not HTTPS).
HttpContent content = new StringContent("{ var1: test }", Encoding.UTF8, "application/json");
var client = new System.Net.Http.HttpClient();
client.Timeout = new TimeSpan(0, 0, 60);
var response = client.PostAsync(url, content);
var jsonResponse = response.Result.Content.ReadAsStringAsync().Result;
double kiloBytesData = (jsonResponse.Length * sizeof(Char)) / 1024d;
But for HTTPS I get:
System.Net.WebException: Error: SecureChannelFailure (Unable to read
data from the transport connection: Connection reset by peer.)
Also note that I can browse to the https url and get the right response so I'm quite certain the WCF/IIS server is behaving as it should.
According to this thread: https://forums.xamarin.com/discussion/69778/restsharp-is-not-working-after-updating-xamarin-studio-to-6-0-1-version
It can be fixed by changing the TLS setting under project properties to Mono instead of Apple. I did that and it made no change.
Yet another site (lost the url now) suggested that it might work (although isen't a good solution) to add this to the AppDelegate.cs FinishedLaunching method to allow all certificates:
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) =>
{
if (cert != null) System.Diagnostics.Debug.WriteLine(cert);
return true;
};
This does not work either and causes the same error.
If it makes any difference, the SSL certificate is from here https://letsencrypt.org/ (awesome service btw).
Any more suggestions as to how one can post and receive json data from an HTTPS bound WCF in Xamarin.iOS?

How to port a curl command to RestSharp? How to troubleshoot?

I have some working curl commands, to a web service, and now I want to move them to a C# program. I am using RestSharp, and trying with the simplest of the web service calls, but just keep getting a generic error message, and I am a bit stumped how to troubleshoot it.
Is there a way to see the headers, and exact URL, that is being sent, and the headers being received?
The curl example is basically this:
curl --user user:pw https://example.com/api/version
And my C# code is:
var client = new RestClient("https://example.com");
client.Authenticator = new HttpBasicAuthenticator("user", "pw");
var request = new RestRequest ("api/version");
var response = client.Execute(request);
Console.WriteLine (response.Content);
Console.WriteLine (response.StatusCode);
Console.WriteLine (response.ErrorMessage);
This gives me:
RestSharp.RestRequest
0
Error getting response stream (Write: The authentication or decryption has failed.): SendFailure
I am using Mono, on Linux. Would that be related? But I could find a few (more advanced) questions with the mono tag on StackOverflow, so it should work. (?)
If it was actually a problem with the username/password, I would get a 403 status, instead of a zero status, I assume?
P.S. In case it matters, the rest of my script is:
using System;
using System.Net;
using RestSharp;
namespace webtest
{
class MainClass
{
public static void Main (string[] args)
{
...(above code)
}
}
}
Regarding troubleshooting
So far I can suggest:
Try commenting out the Authenticator line to see if anything changes (in my case it did not)
Try http://google.com
Try https://google.com
That was enough for me to see that http URLs work, https URLs fail.
(If you need more troubleshooting, and are using https, the sender parameter shown below contains various fields about the request being sent to the remote server.)
Regarding porting curl commands
By default curl on linux uses the certificates it finds in /etc/ssl/certs. The blanket equivalent for Mono is to do mozroots --import --ask-remove, which will import all certificates (see Mono security FAQ).
Another way to do it is by putting this at the very top of your program:
ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => {
//Console.WriteLine(certificate.ToString());
return true;
};
The commented line can be used to report the certificate to the user, interactively get their approval, or to check the certificate fingerprint against the expected one. By simply returning true it means all certificates are trusted and unchecked.
Bonus: Cert checks
Here is one way to check for a specific certificate:
ServicePointManager.ServerCertificateValidationCallback +=
(sender,certificate,chain,sslPolicyErrors) => {
if(((System.Net.HttpWebRequest)sender).Host.EndsWith("google.com") ){
if(certificate.GetCertHashString() == "83BD2426329B0B69892D227B27FD7FBFB08E3B5E"){
return true;
}
Console.WriteLine("Uh-oh, google.com cert fingerprint ({0}) is unexpected. Cannot continue.",certificate.GetCertHashString());
return false;
}
Console.WriteLine("Unexpected SSL host, not continuing.");
return false;
}

Ignore invalid SSL (version 3) certificate with XmlTextReader https request

I am trying to create an XML document from an https web request, but I am having trouble getting it to work when the site has an invalid certificate. I want my application to not care about the certificate, I want it to live it's life on the edge without fear!
Here is the initial code I had, which I have used before may times to get what I want from a standard http (non SSL) request:
XmlDocument xml = new XmlDocument();
XmlTextReader reader = new XmlTextReader("https://www.example.com");
xml.Load(reader);
With the site having an invalid SSL certificate I am now getting the following error:
The request was aborted: Could not create SSL/TLS secure channel.
Now I have done my Google-ing and tried a number of promising solutions but it seems to be of no help.
One I tried here on SO looked good but didn't seem to work, I added the 'accepted answer' line of code directly before my code above as it wasn't too clear as where it should go.
In case it makes any difference, my code is in a class library and I am testing via a console app. I am also using .Net 4.
Here is my latest attempt (which does not work):
XmlDocument xml = new XmlDocument();
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback((s, ce, ch, ssl) => true);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlCommand);
request.Credentials = CredentialCache.DefaultCredentials;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream receiveStream = response.GetResponseStream())
{
XmlTextReader reader = new XmlTextReader(receiveStream);
xml.Load(reader);
}
}
OK so I have found the solution. We had opted to try and disable the SSL on the server for testing and noticed it was using SSL 3. After another Google search I found some additional code to fix the issue (important to set the SecurityProtocolType):
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback((s, ce, ch, ssl) => true);
XmlDocument xml = new XmlDocument();
XmlTextReader reader = new XmlTextReader(urlCommand);
xml.Load(reader);
Hm, maybe the XmlTextReader uses a different way of accessing the HTTPS.
Try making the web request with a HttpWebRequest, and pass response.GetResponseStream() to the XML text reader (and leave the ServicePointManager.ServerCertificateValidationCallback override where you had it)
Reason for this error:
You are not using valid client certificate on your website.
You could try below:
For quick turn around you could try access the URL with http://_____ * - it's not recommended but for testing you could try.*
You are not using valid client certificate hence, you could write something like below:
Add this line where you are requesting to download XML or making some http request.
//Add Mock certificate validation
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnValidationCallback);
Add below as a global method:
public static bool OnValidationCallback(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
{
return true;
}

How do I use SSL certificates with HttpWebRequest in C#?

Currently I'm writing a utility application that will connect to a given IP and port and check the information in the SSL certificate using HttpWebRequest. When I try to extract the certificate I get an error that an exception was thrown. The exception seems to be because the act of coping the SSL certificate seems to trigger yet another validation check.
Here is the code, and maybe someone can either show me a better way to do this or if I am missing something. I don't care if the SSL Certificate is expired or doesn't match the URL. None of that is relevant for what I'm doing.
When I assign the X509Certificate in the delegate to a new variable, and look at the variable in the debugger, all the properties show SSLCert.Issuer threw an exception of type 'System.Security.Cryptography.CyrptographicException'
When I try to access a property of SSLCert, I get the following Exception thrown: m_safeCertContext is an invalid handle
I'be searched for that exception, but everything points to an invalid certificate, which might be true if the certificate is expired, and might be true for the IP and port combination I am connecting to. But since I am connecting to the IP using the IP and not anything that would match the common name, I expect that and could care less as I still need the information.
The code is below, I put some comments in as well for what doesn't work and what does work.
// To get around the SSL validation in the HttpWebRequest
System.Net.ServicePointManager.ServerCertificateValidationCallback =
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// The below works but isn't what I want. CertName and ExpireDate are both Strings
this.CertName = ProcessSubject(certificate.Subject);
this.ExpireDate = certificate.GetExpirationDateString();
// The below works but the X509Certificate SSLCert shows exceptions in the debugger for most of the properties.
this.SSLCert = certificate;
return true; // **** Always accept
};
HttpWebRequest myRequest = (HttpWebRequest)System.Net.WebRequest.Create("https://" + this.IP + ":" + this.Port + "/SSLCheck.html");
myRequest.KeepAlive = false;
myRequest.Method = "GET";
try
{
HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
}
catch (Exception e)
{
if (e.Message != "The remote server returned an error: (404) Not Found.")
{
throw Exception("Error");
}
}
// THE BELOW FAILS
this.CertName = this.SSLCert.Subject;
This is just a guess on my part. WHat I think is happening is that the certificate info coming through in the SSL handshake is used by .NET to create a cert object that is passed to you in the delegate. The delegate is only to be used for the purpose of validation. After the call completes, .NET closes the safe handle of the certificate. THat is why the call fails when trying to access the certificate outside the callback.
TO get around this, you can serialize the certificate inside the delegate, and deserialize it outside the delegate.
(Edit from our comments below)
I suppose what you need to do is create a custom class and store the data you need in it inside the delegate code, rather than pass around the actual certificate reference.

Categories