How to port a curl command to RestSharp? How to troubleshoot? - c#

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;
}

Related

How to make my C# client application to accept my NodeJS server certificates

I am following this tutorial to make PEM certificates working between a NodeJS Server and a NodeJS Client.
My situation is similar, at server I have a NodeJS server, but, at client side, I have a C# Client application (.NET Framework 4.7.2).
Just for a test, at C# Client application we have:
bool pingSuccess = false;
using (var wb = new WebClient())
{
string response = wb.UploadString(nodeJSURL, "POST", "{\"message\":\"ping\"}");
if (response == "success") pingSuccess = true;
}
if (!pingSuccess) throw new HttpListenerException(503, "Il server NodeJS risulta non raggiungibile dall'indirizzo " + nodeJSURL);
But (obviously) I get this expected error:
Web Client Exception: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel
What I tried to do, is to try applying the procedure used by Anders Brownworth in his tutorial for its NodeJS Client adapting this code to C#:
var fs = require('fs');
var https = require('https');
var options = {
hostname: 'localhost',
port: 4433,
path: '/',
method: 'GET',
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function(res) {
res.on('data', function(data) {
process.stdout.write(data);
});
});
req.end();
But I am quite new in C#, how can I rewrite this code to be equivalent and use it in C#?
In other words, what I need is for my C# client code to accept the certificates provided by my NodeJS server. I looked for other solutions, but I found them too complex, is there a lean and clean way to do it?
I tried a workaround, adding following line just before the HTTP request:
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
works, but this is not what I need, because I want to trust just only the certificates provided by my NodeJS server. However thanks to this workaround I was able to verify that the rest of the application is working properly.
Sidenote: I created my PEM certificates following the already abovementione tutorial but using Wind64 OPENSSL and it works perfectly n Windows 10.

SSL TLS communication in c# with self signed certificate not working

I have a .pem certificate file which is used to communicate between two servers. For communication I have written a program in C# like this:
var client = new RestClient("https://aaaaa.com:1111");
client.ClientCertificates = new X509CertificateCollection();
client.ClientCertificates.Add(new X509Certificate(#"C:\Users\aaa\Desktop\bbb.pem"));
var request = new RestRequest("/qqq/www", Method.POST);
request.AddJsonBody(new { create = new { msgBdy="Test" } });
var response = client.Execute(request);
Console.WriteLine(response.StatusCode);
//The underlying connection was closed: An unexpected error occurred on a send.
When I post the request through SoapUI it goes through, but when I try to send it through Postman or the above C# program it doesn't.
Screenshot from wireshark is below:
The change cipher spec event is called for the successful API call but through postman and c# application this event is never called.
I have tried to do this as explained in this article as well https://www.codeproject.com/Articles/326574/An-Introduction-to-Mutual-SSL-Authentication but that also didn't work.
How can I fix this issue.

How to detect SSL Policy errors with .net?

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;

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.

How to perform a fast web request in C#

I have a HTTP based API which I potentially need to call many times. The problem is that I can't get the request to take less than about 20 seconds, though the same request made through a browser is near instantaneous. The following code illustrates how I have implemented it so far.
WebRequest r = HttpWebRequest.Create("https://example.com/http/command?param=blabla");
var response = r.GetResponse();
One solution would be to make an asynchronous request but I would like to know why it takes so long and if I can avoid it. I have also tried using the WebClient class but I suspect it uses a WebRequest internally.
Update:
Running the following code took about 40 seconds in Release Mode (measured with Stopwatch):
WebRequest g = HttpWebRequest.Create("http://www.google.com");
var response = g.GetResponse();
I'm working at a university where there might be different things in the network configuration affecting the performance, but the direct use of the browser illustrates that it should be near instant.
Update 2:
I uploaded the code to a remote machine and it worked fine so the conclusion must be that the .NET code does something extra compared to the browser or it has problems resolving the address through the university network (proxy issues or something?!).
This problem is similar to another post on StackOverflow:
Stackoverflow-2519655(HttpWebrequest is extremely slow)
Most of the time the problem is the Proxy server property. You should set this property to null, otherwise the object will attempt to search for an appropriate proxy server to use before going directly to the source. Note: this property is turn on by default, so you have to explicitly tell the object not to perform this proxy search.
request.Proxy = null;
using (var response = (HttpWebResponse)request.GetResponse())
{
}
I was having the 30 second delay on 'first' attempt - JamesR's reference to the other post mentioning setting proxy to null solved it instantly!
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_site.url);
request.Proxy = null; // <-- this is the good stuff
...
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Does your site have an invalid SSL cert? Try adding this
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AlwaysAccept);
//... somewhere AlwaysAccept is defined as:
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
public bool AlwaysAccept(object sender, X509Certificate certification, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
You don't close your Request. As soon as you hit the number of allowed connections, you have to wait for the earlier ones to time out. Try
using (var response = g.GetResponse())
{
// do stuff with your response
}

Categories