WebException SecureChannelFailure when invoking web service over https from Xamarin Droid app - c#

I'm creating a Xamarin.Droid app with Visual Studio (C#).
I have a RESTful web service (WebAPI) from which I want to retrieve some data in a Xamarin Droid project, using HttpWebRequest. The fact that it's WebAPI and HttpWebRequest is not the key as I had the exact same problem (described below) with some older WSE 3.0 web services and the use of HttpClient in the generated proxies. In the case of those WSE 3.0 web services we solved the problem by extending the generated proxy class to use ModernHttpClient instead. We didn't realize this problem manifests outside of HttpClient. I now think we must be doing something wrong, as opposed to blaming HttpClient.
In the current iteration, connecting to WebAPI, I'm using HttpWebRequest. Whether using HttpWebRequest or HttpClient, everything works fine as long as I'm connecting via http. As soon as I try to connect via https the request fails. For clarification, the web server hosting the web service does have a valid certificate (it's not expired, and has a valid CA.)
I have not been able to find any solution to this problem, despite a ton of Googling. I can barely find references to other people having this problem, much less a solution, which makes me think there must be something wrong in what I'm doing.
Basically, you can this method to a Xamarin.Forms Droid project and call it. If url is using https then request.GetResponse() throws a WebException: "Error: SecureChannelFailure (The authentication or decryption has failed.)". If url is using plain http then the call executes cleanly and returns the expected data.
using System;
using System.IO;
using System.Net;
using Android.App;
using Android.Content.PM;
using Android.OS;
// ...
private string FetchData()
{
// Public test server that's handy for testing JSON code.
// http://jsonplaceholder.typicode.com
string url = "https://jsonplaceholder.typicode.com/posts/1";
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 10 * 1000; // 10 seconds
request.Accept = "application/json";
request.Method = "GET";
string result = "";
try
{
using (WebResponse response = request.GetResponse())
{
result = "Presumably, do something with response.GetResponseStream()";
}
}
catch (Exception ex)
{
result = $"{ex.GetType().Name}: {ex.Message}";
}
return result;
}
The basic pattern there comes from a Xamarin "recipe": Call a REST Web Service. I changed it to be synchronous because when I change request.GetResponse() to request.GetResponseAsync() (and async-await) then the call either times out, or hangs indefinitely, obfuscating the underlying WebException (SecureChannelFailure).
This is a simple HTTP GET, and any browser, or REST/SOAP utility (e.g. "Simple REST Client" for Chrome) can be used to build and send that exact same request, and it works fine with both http and https. Similarly, I can take that code above and paste it into a Windows Console application and it works fine with both http and https.
For the record, love it or hate it, I have also tried adding a delegate of { return true; } to the System.Net.ServicePoint.ServerCertificateAuthenticationCallback event. To me that wouldn't be a solution anyway, but it might help diagnose the problem. Too bad a breakpoint set in that delegate is never tripped, indicating my code never even gets to that point.
It seems impossible to me that invoking web services through https is not supported in a Xamarin Droid app, so I'm going on the assumption that I'm doing something wrong. What could be causing this failure in Xamarin Droid, when it works fine in a browser, and in a plain Windows Console app?

if you call a HTTP url and get this exception , be sure set redirect to false:
request.AllowAutoRedirect = false;
if you call a HTTPS url then put these lines before your request code:
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

Related

REST Connection using UnityWebRequest via proxy

I am trying to make a REST request to a server via proxy using UnityWebRequest. I have tried to use
Network.useProxy = true;
but this fails with the error
Cannot connect to destination host
Network.useProxy has been removed in Unity 2018.2.12. I am able to connect over Postman and curl. Can anybody confirm that UnityWebRequest does not support connection via proxy?
Even if it hasn't been deprecated, it wouldn't still work because Unity's Network.useProxy is used for the legacy networking system and the UnityWebRequest API is not part of that.
There is no support for proxy with UnityWebRequest and there is no plan to add support for this on Unity roadmap. Vote for its support here.
Your only workaround is to use one of the standard C# web request API such as HttpWebRequest and WebClient. They are supported in Unity. With HttpWebRequest, you can use proxy as below:
string proxyHost = "192.168.1.3";
int proxyPort = 8080;
string url = "http://YourUrl.com/blah";
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Proxy = new WebProxy(proxyHost, proxyPort);
Since this is not UnityWebRequest, you have to do that in another Thread to prevent blocking your game main Thread.

Call Dynamics Web API in C# Plugin

I have a Business Process Flow in Microsoft Dynamics to handle creation of a new client. When the process finishes, I am attaching a workflow that kicks of an action which calls a plugin to do some custom processing. I am following this article to set up this process.
In my plugin, I have a call to the Dynamics Web API (see code below). When I hit the responseMessage = client.GetAsync(url).Result; line, my plugin exits without returning an error. When I am debugging in the Plugin Registration Tool, the Plugin Registration Tool crashes and has to be restarted. When I look at the network traffic, this call appears to be failing with a 401 - Unauthorized error.
When I try this same HttpClient call from a console app, the call succeeds. I've tried a few different credentials for authentication without success. I've also tried calling the GetAsync function a few different ways. Is this error related to the async or authentication methods? What am I missing here?
HttpClient client = new HttpClient(new HttpClientHandler() { Credentials = new NetworkCredential("admin", "password", "DOMAIN") });
client.BaseAddress = new Uri(Helpers.GetSystemUrl(COHEN.APIConnector.Application.Dynamics));
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
client.DefaultRequestHeaders.Add("OData-Version", "4.0");
HttpResponseMessage responseMessage;
string url = "ccseq_clients";
responseMessage = client.GetAsync(url).Result;
Update
We are using Dynamics 2016 On-Premise.
The reason we are using the Web API is that this call occurs in a library we have developed that will interface between multiple systems. The library is used within a plugin, but can also be used outside of a plugin for other applications. The code sample we provided is the chunk of code that is failing in the library. We pulled the chunk of code directly into the plugin to see if the error was related more to the library or the plugin.
We noticed that the client.GetAsync(url).Result call was actually returning a correct value. We missed seeing in our network traffic that two calls failed before a third succeeded and returned the correct value (See screenshot below). The odd thing is that when we debug this line of code, stepping over the line of code causing the Plugin Registration Tool to crash while the network traffic shows we returned the correct values. If we set a breakpoint after this line of code, then the Plugin Registration Tool does not crash and we have a correct value in our response. This behavior changes when we went back to debugging in the library rather than directly in the plugin. In the library, the Plugin Registration Tool always crashes when hitting this line regardless of where we set our breakpoints even though the network traffic still shows a successful response.
Update 2
It would appear that my library is successfully making multiple different calls to the Web API so I'm thinking the issue is more that the Plugin Registration Tool can't handle the Web API call more than the call not actually working.
I believe the issue was with the Plugin Registration Tool profiling a plugin execution. My follow up question is here.
This blog states that we can make Web Api work in CRM on-premise (non-IFD) plugin by using WebClient with DefaultCredentials.
using (WebClient client = new WebClient())
{           
client.UseDefaultCredentials = true;
    byte[] responseBytes = client.DownloadData(new Uri("<your web api url>/accounts(3C2D2712-E43F-E411-9402-005056AB452C)"));
    string response = Encoding.UTF8.GetString(responseBytes);
    // parse the json response
}
Make sure you specify the UseDefaultCredentials as true to make the web api call under the context of the user the plugin is running.
We may struggle more with Online plugins because of Sandbox + Adal library + AAD tokens combo as we are trying in a non-interactive plugin code unlike other interface where we can challenge the user by prompt.
Moreover Web api is more useful for cross platform, outside CRM context integration. Inside the platform execution you can use Org service to achieve more.

HttpClient and WebClient give 401 error yet direct link in browser works

I'm the middleman on an integration project between a Woocommerce site and a retail POS system. The POS is trying to synchronize products with the Woocommerce store. The site has an SLL certificate so all communication is over SSL. The POS vendor tells me they are using TLS1.2. We have a URL that contains a customer key and secret so I can't paste the full URL here, but the problem is as follows...
The same URL pasted directly into the browser works perfectly well, returning the expected JSON payload. The same URL used with a .NET WebClient or HttpClient returns a 401 Not Authorized error. Here is an example of an integration test using WebClient;
[Test]
public void DownloadString_UsingWebClient_ReturnsNonEmptyResponse()
{
using (var client = new WebClient())
{
var response = client.DownloadString("https://demostore.mycompany.com.au/wp-json/wc/v2/settings/tax/woocommerce_prices_include_tax?consumer_key=ck_xxxskeyxxx&consumer_secret=cs_xxxsecretxxx");
Assert.IsNotEmpty(response);
}
}
And here is a similar test using HttpClient;
[Test]
public async Task DownloadString_UsingHttpClient_ReturnsNonEmptyResponse()
{
using (var client = new HttpClient())
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var response = await client.GetStringAsync("https://demostore.mycompany.com.au/wp-json/wc/v2/settings/tax/woocommerce_prices_include_tax?consumer_key=ck_xxxskeyxxx&consumer_secret=cs_xxxsecretxxx"");
Assert.IsNotEmpty(response);
}
}
Both tests never make it to the Assert. Just to reiterate, if I take the same URL and paste it into my browser, everything works as expected. I've tried a couple of different things, including enforcing the security protocol type (as in the latter example), but it is all just a poke in the dark, really.
The website is using the latest version of Wordpress, and it is in a shared linux hosting environment.
Is there something I'm missing with WebClient and HttpClient? Do I go back to the web hosting company and find out if there is something in the configuration that would prevent a .NET client from performing the same as a browser (I have logged a support ticket to this effect, but I'm not getting any headway)?
If you're in a corporate network and the browser works and your code doesn't, it's likely you're missing the Proxy settings for the WebClient or HttpClient instance.
I typically pass a HttpClientHandler instance with Preauthenticate = true and UseDefaultCredentials = true to the HttpClient.
I found that usually when something works in the browser, but not outside of it, that suggests that the API relies on User-Agent header being present in the request.
One of the reasons for this may be a reverse-proxy/firewall in front of that API.

Strange Error 400 while using HttpRequest to consume REST API

I am trying to consume a REST API and i have an issue that it is driving me crazy...
I created a dll to wrap the service consumption layer, and i found that if i consume the services using the c# interactive feature it works fine.
The issue is when i try to consume it from another DLL, it is throwing Bad Request exception when executing GetResponse()
The code executed is...
var url = $"{_salesForceInstance}/services/data/{_salesForceVersion}/query/?q=SELECT+Id,Name,AccountId+from+Contact+WHERE+Email+=+'{email}'";
var webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.ServicePoint.CloseConnectionGroup(webRequest.ConnectionGroupName);
webRequest.Method = "GET";
webRequest.ContentType = "application/json";
webRequest.Headers.Add("Authorization", $"Bearer {_authorizationToken}");
var webResponse = webRequest.GetResponse() as HttpWebResponse;
I also when debugging code, to send the same through POSTMAN and it works fine...
Any ideas??? I am quite frustrated at this point why it works when consuming the dll from C# interactive but not from another dll...
As spender suggested i tried with Fiddler...
I found that despite of the fact both request looks the same, (the one from c# interactive and the one from the console app), in the one that returned the HTTP 400 error code the response included the following message
"[{"message":"TLS 1.0 has been disabled in this organization. Please use TLS 1.1 or higher when connecting to Salesforce using https.","errorCode":"UNSUPPORTED_CLIENT"}]"
That message does not appear when debugging...
Finally i solved by including the line described below in my code in order to use TLS 1.2
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Now it works !!!
Hope this helps to someone else!
Thanks!

HTTP Requests (POST) doesn't works correctly in a DLL Library?

I'm trying to use TweetSharp in a DLL Library.
When I execute the following code in a DLL Library, the response returns invalid (417 Unknown):
var verify = FluentTwitter.CreateRequest()
.AuthenticateWith("<HIDDEN>",
"<HIDDEN>",
"<HIDDEN>",
"<HIDDEN>")
.Statuses().Update("It works!");
var response = verify.Request();
When I execute the same code in a Windows Application, the response returns correctly.
Is there any thing (configuration) that could process HTTP Requests (using POST) differently using the same code in DLL Library and Windows Application?
most likely, the library has the "Expect100Continue" set to true. It should be set to false.
http://groups.google.com/group/twitter-api-announce/browse_thread/thread/7be3b64970874fdd
Looks as though this library needs to be updated. This was changed about 10 months ago.
So the code should something like this...
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(newURL);
request.Method = "POST";
request.ServicePoint.Expect100Continue = false;
request.ContentType = "application/x-www-form-urlencoded";
TweetSharp definitely handles the Expect100Continue issue, so something else is at play here. What does your policy file look like? You need to make sure you can send HTTP requests from DLLs if you're in a medium trust environment by modifying your policy.
Probably its current logged user related
Try to log with your website application pool user and run your code again

Categories

Resources