Make HTTPS request using HttpClient in Xamarin.iOS - c#

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?

Related

C#.Net Core 3.1 HttpRequest timeout or SSL connection issue

Ok i have the following code
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
clientHandler.UseProxy = false;
using (HttpClient client = new HttpClient(clientHandler))
{
var reqUri = string.Format("{0}/{1}?getBIC=true&validateBankCode=true", AppSettings.Instance.ValidationSettings.IBANValidationUri, iban);
Logger.Instance.LogInfo("IBAN Validation: Sending request");
using (HttpResponseMessage res = await client.GetAsync(reqUri))
{
using (HttpContent content = res.Content)
{
if (content != null)
{
Logger.Instance.LogInfo("IBAN Validation: Reading data");
var data = res.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(data.Result))
{
result = JsonConvert.DeserializeObject<IBANValidationResult>(data.Result);
}
}
}
}
}
Now this code simply makes a rquest to the openiban api to get the specific bank information from a IBAN number.
This code runs on a ASP.NET Core 3.1 Web API that is running on a IIS Server.
Now we have 3 exact same server with the same web api.
The problem:
when i call the service from a browser on server 1, the service works fine i get the response etc.
when i call the service from a browser on server 2 it works also fine.
when i call the service from a browser on server 3 it takes too long and then i receive following error on the backend:
ERROR: The SSL connection could not be established, see inner
exception
ERROR: Authentication failed because the remote party has closed the
transport stream.
The funny thing is: This issue does not depend on one server, sometimes the server 3 works but then server 2 does not work, sometimes server 1 does not work an the other servers are working.
Does anybody have a clue what is going on here?
PS: The SSL certificates are installed on the server
Thanks a lot

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.

Consuming J2EE web service signing request but receiving unsigned response

I'm consuming a third party J2EE web service that requires sign the request with a certificate, but the web service is responding an unsigned response.
This is the way I'm doing the request:
public static WcfServiceNamespace.ResponseType GetResponse(X509Certificate2 certificate)
{
var request = GetExampleRequest();
var endPoint = new EndpointAddress($"https://endPoint.url/contoso");
var binding = GetCustomBinding();
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
using (var client = new WcfServiceNamespace.ServicePortTypeClient(binding, endPoint))
{
client.Endpoint.Contract.ProtectionLevel = ProtectionLevel.Sign;
client.ClientCredentials.ClientCertificate.Certificate = certificate;
return client.ProcessRequest(request);
}
}
private static Binding GetCustomBinding()
{
var c = new CustomBinding();
var version = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
var sec = SecurityBindingElement.CreateCertificateOverTransportBindingElement(version);
sec.EnableUnsecuredResponse = true;
sec.AllowInsecureTransport = true;
sec.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
c.Elements.Add(sec);
c.Elements.Add(new TextMessageEncodingBindingElement() {MessageVersion = MessageVersion.Soap11});
c.Elements.Add(new HttpsTransportBindingElement() { RequireClientCertificate = true });
return c;
}
The java web service is responding correctly the request without any header:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<!-- correct response -->
</soapenv:Body>
</soapenv:Envelope>
But WCF client is throwing an exception when it tries to process the response:
System.ServiceModel.Security.MessageSecurityException: 'Cannot find a token authenticator for the 'System.IdentityModel.Tokens.X509SecurityToken' token type. Tokens of that type cannot be accepted according to current security settings.'
I already tried this configuration:
WCF - Cannot find a token authenticator for X509SecurityToken
But it does not resolve my problem because the header of the response is totally empty as I explained before and the endpoint is using https but has no certificate to trust.
My question is:
Is there any way to configure WCF to correctly sign the request but ignore the response security?
Edit:
I already tried this questions:
IBM DataPower 3.7.1.x issues with WCF clients
WCF error calling WS-Security web service: Cannot find a token authenticator for the X509SecurityToken
But the answers don't help
Edit:
I make it work with WSE3 but I want use a newer technology. If it works in WSE3, Why not in WCF?
Can we use the below tools to generate the SOAP client, with which we can call the service like the below method?
https://learn.microsoft.com/en-us/dotnet/framework/wcf/servicemodel-metadata-utility-tool-svcutil-exe
It is a built-in tool in Visual Studio, and we can generate the client proxy class and the compatible custom binding and security mode configuration by using the SVCUtil command. the client proxy will automatically use the configuration compatible with the server to create the request when instantiating.
Alternatively, we could generate the client proxy by means of the Add service reference menu.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
I have never called the J2EE web service, please let me know if it helps.

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;

C# Windows Store App HTTPClient with Basic Authentication leads to 401 "Unauthorized"

I am trying to send a HTTP GET request to a service secured with BASIC authentication and https. If I use the RESTClient Firefox plugin to do so there is no problem. I am defining the basic-header and sending the GET to the url and I am getting the answer (data in json).
Now I am working on a Windows Store App in C# which is meant to consume the service. I enabled all required capabilities in the manifest and wrote the following method:
private async void HttpRequest()
{
string basic = "Basic ...........";
Uri testuri = new Uri(#"https://...Servlet");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", basic);
Task<HttpResponseMessage> response = client.GetAsync(testuri);
var text = await response;
var message = text.RequestMessage;
}
I tried out many different possibilites like getting the response-string but everything lead to an 401 Status Code answer from the Server.
I looked at many similar problems and my understanding of the communication is the following: Client request -> Server response with 401 -> Client sends Authorization header -> Server response with 200 (OK)
What I don't understand is why I am getting the 401 "Unauthorized" Status Code although I am sending the Authorization header right at the beginning. It would be interesting if someone knows how this is handled in the RESTClient.
The BASIC header is definetly correct I was comparing it with the one in the RESTClient.
It would be great if someone could help me with this.
Thanks in advance and kind regards,
Max
Was having a similar problem, i added a HttpClientHandler to HttpClient.
var httpClientHandler = new HttpClientHandler();
httpClientHandler.Credentials = new System.Net.NetworkCredential("","")
var httpClient = new HttpClient(httpClientHandler);
Credentials should be encoded, before adding to the header. I tested it in WPF app, It works...
string _auth = string.Format("{0}:{1}", "username", "password");
string _enc = Convert.ToBase64String(Encoding.UTF8.GetBytes(_auth));
string _basic = string.Format("{0} {1}", "Basic", _enc);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization",_basic);

Categories