I'm trying to test an API call on my local machine, using RestSharp, with the following code...
var client = new RestClient("https://[API URL]");
var request = new RestRequest( Method.POST);
request.AddParameter("session", this, ParameterType.RequestBody);
IRestResponse<SessionOut> response = client.Execute<SessionOut>(request);
return response.Data.session.id;
In response I get the an error telling me that the request was aborted because it "could not create SSL/TLS secure channel".
Does this mean I need to try and set up https://localhost instead of http://localhost in order to call APIs at https:// addresses?
UPDATE
I have updated my code to the following, as per #Shai_Aharoni's answer below. I am still getting the same error however.
string pathToYourClientCert = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "[my certificate file");
var client = new RestClient("[API URL]/");
client.ClientCertificates = new X509CertificateCollection();
client.ClientCertificates.Add(new X509Certificate(pathToYourClientCert));
var request = new RestRequest( Method.POST);
request.AddParameter("session", this, ParameterType.RequestBody);
IRestResponse<SessionOut> response2 = client.Execute<SessionOut>(request);
Try adding this to your code:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Related resources:
Some useful information about setting TLS 1.2 in another stackoverflow post
Well... There are a few steps that you need to complete before you can call you HTTPS endpoint.
1) Make sure that your server supports an HTTPS endpoint (i.e : that the URL https://[APIURL] is reachable.
2) Have a valid server (the api server) certificate installed on the machine that executes the HTTPS call.
3) Add the certificate to your RestSharp client. Similar to something like this:
string pathToYourClientCert = "cer/cert.cer";
client.ClientCertificates.Add(new X509Certificate(pathToYourClientCert));
Hope this helps...
Follow these steps:
https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/http-stack?tabs=macos
Also add this to your code in MainActivity.cs -> OnCreate -> Before you load your app:
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
I had to do both for my API requests to work in Xamarin Forms!
Make sure your CERTIFICATE is not SELF-SIGNED!!!
Related
C# SSL security certificate wizard needed!
Postman works, SoapUI works, my code gets a 404!
I have created a C# 4.7.2 MVC Web application. It makes a RestRequest().Post to a C# WebApi hosted on iis. This all works properly and as expected on http.
Now I want to make it https, I have added a security certificate to iis. Now I have problems.
If I make the request via Postman or SoapUI I get the response as before no problem. When I make the call with my C# HelperApp I get a 404 back! (Of course I checked the address path a thousand times.)
The 404 really confuses things. It's definitely there and we are hitting it successfully with Postman and SoapUI.
I suspect that I am not telling the RestClient to use SSL?
TIA.
case "Submit":
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3 | SecurityProtocolType.SystemDefault;
var apiServer = Startup.Config.ApiUrl; // this points to web.config <add key="API_URL" value="https://xxx.yyy.local:444/" />
//It works when I use <add key="API_URL" value="http://IEAVNBQ02:85/" />
var baseUrl = apiServer + $"api/ProcessQuotes/";
var client = new RestClient(baseUrl);
client.Options.MaxTimeout = -1;
var request = new RestRequest();
request.Method = Method.Post;
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", model.JsonRequest, ParameterType.RequestBody);
var response = client.Execute(request);
var Response is populated with:
<title>Network Error: 404</title>
<meta name="description" content="Proxy Exception: Network Error: 404" />
You may need to set
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
before making a call.
Just keep in mind this is a global setting.
The company I work for have a limited internet connection and we use the proxy (example: 10.10.10.10:8080) to access at some restricted connections.
I can use the API in Postman (putting the proxy in the Postman settings) but when putting in the C# WebClient code it gives me a 403-Forbidden error.
I only need the var sensorData field but I splitted in var data and var data2 to understand where was the problem. It gives me the error at the var data = ...
Uri uri = new Uri("https://XXXXXXXX/api/DatasourceData/DatasourceDataHistoryBySerialNumber/");
Token token = new Token();
token = GetToken(tokenAPI);
using (WebClient client = new WebClient())
{
try
{
client.Proxy = new WebProxy("10.10.10.10", 8080);
client.Headers.Add("Authorization", "Bearer " + token.AccessToken);
client.QueryString.Add("serialNumbersDatasource", "I2001258");
client.QueryString.Add("startDate", string.Format("{0:s}", "2019-12-01"));
client.QueryString.Add("endDate", string.Format("{0:s}", DateTime.Now));
client.QueryString.Add("isFilterDatesByDataDate", "false");
var data = client.DownloadData(uri);
var data2 = (Encoding.UTF8.GetString(data));
sensorData = (JsonConvert.DeserializeObject<List<Sensor>>(Encoding.UTF8.GetString(client.DownloadData(uri))))[0];
}
}
Seems the problem at this line
client.Headers.Add("Authorization", "Bearer " + "tokenTest");
here you wil add header Authorization with value Bearer tokenTest
so, 403 Forbidden returns by service which you are addressing, but not a proxy
change to
client.Headers.Add("Authorization", "Bearer " + tokenTest);
and check if tokenTest has valid value
Check to see if you need any additional properties on the proxy. You may possibly need to enable:
UseDefaultCredentials (Boolean) true if the default credentials are
used; otherwise, false. The default value is false
Also, check your full url and query string that you are producing - look at the outgoing request fabricated (in the debugger) or through Fiddler and make sure it all lines up, url, query string, headers, etc.
From the docs:
Address
Gets or sets the address of the proxy server.
BypassArrayList
Gets a list of addresses that do not use the proxy server.
BypassList
Gets or sets an array of addresses that do not use the proxy server.
BypassProxyOnLocal
Gets or sets a value that indicates whether to bypass the proxy server for local addresses.
Credentials
Gets or sets the credentials to submit to the proxy server for authentication.
UseDefaultCredentials
Gets or sets a Boolean value that controls whether the DefaultCredentials are sent with requests.
Probably a problem with authorization header.
Is the token valid? Does it work with the same token in Postman?
I bet the api can't validate the token and and gives you no authorization to the resources. This is what a 403 would mean (but don't know what the api programmer actually intended by giving you 403).
Do you have access to the api's source code?
The token is really a string "tokentest" and that works with Postman?
I would suggest you to go for xNet.dll instead of webclient Because xNet library are considered best for proxy and webrequest.
var request = new HttpRequest()
request.UserAgent = Http.ChromeUserAgent();
request.Proxy = Socks5ProxyClient.Parse("10.10.10.10:8080");//can use socks4/5 http
Based on this
Try adding User-Agent in the header
client.Headers.Add("User-Agent", "PostmanRuntime/7.26.1");
In my case i did not specify security protocol. Paste this line of code before running any WebClient requests.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
original answer: How to specify SSL protocol to use for WebClient class
I have a service in my C# application which uses App Center's api to push notifications. All my request come back with 401s (Unauthorised) yet, when I used the same details on postman i.e. content, header auth, owner_name and app_name it works successfully and sends the application.
This is very confusing and I am wondering if Postman handles some extra bits and pieces which I am missing out.
C# Push Notification Service
private async Task<bool> PostHttpRequest(PushNotificationModel pushNotificationModel)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("X-API-Token", _appCenterApiToken);
var url = "https://api.appcenter.ms/v0.1/apps/myowner/myapp/push/notifications";
var content = JsonConvert.SerializeObject(pushNotificationModel);
var buffer = Encoding.UTF8.GetBytes(content);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _httpClient.PostAsync(url, byteContent);
return response.IsSuccessStatusCode;
}
I debugged this code and used the same data being passed to the client to use on Postman, hence why Model data is absent for example.
Help is much appreciated!
Put the code below in your startup form.
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3 | System.Net.SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
I figured out the issue using Telerik Fiddler. This helpful tool allows you to actively view HTTP traffic to and from your machine. Now the mistake was a silly one on my part but it boiled down to changing this:
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("X-API-Token", _appCenterApiToken);
to:
_httpClient.DefaultRequestHeaders.Add("X-API-Token", _appCenterApiToken);
As the first one was actually appearing in the HTTP header as Authorization : X-API-Token apiToken rather than X-API-Token : apiToken it caused the request to be bad, hence returning 401 (Unauthorised)
Scenario
Win10 x64
VS2013
I'm trying to make a WebRequest, but I'm getting the following error:
The underlying connection was closed: An unexpected error occurred on a send.
Digging into the inner exception, I got:
"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host."
The code which does the request is the following:
private static Hashtable exec (String method, String uri, Object data, String contentType) {
Hashtable response;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create (API_BASE_URL + uri);
request.UserAgent = "MercadoPago .NET SDK v"+MP.version; //version resolves to 0.3.4
request.Accept = MIME_JSON; // application/json
request.Method = method; //GET
request.ContentType = contentType; //application/json
setData (request, data, contentType); //setData in this case does nothing.
String responseBody = null;
try {
HttpWebResponse apiResult = (HttpWebResponse)request.GetResponse (); //Error throws here
responseBody = new StreamReader (apiResult.GetResponseStream ()).ReadToEnd ();
response = new Hashtable();
response["status"] = (int) apiResult.StatusCode;
response["response"] = JSON.JsonDecode(responseBody);
} catch (WebException e) {
Console.WriteLine (e.Message);
}
}
What i've already done:
Made the request via Console Application and MVC Application controller. Both throws the same exception
Called the API via Postman with the exact same headers, which brings me the content correctly.
Those requests were working okay via c# about 4 days ago and I suddenly started having issues, but considering the fact that it responds okay for Postman, I can't figure out where's the problem.
Here's Postman's response
EDIT: Did both requests with Fiddler listening. The result for Postman shows a direct request to the API with HTTPS. When trying with my ConsoleApplication, it shows a HTTP request, which makes a tunnel to the API endpoint, port 443.
The TextView from Fiddler for the tunnel request says the following:
I noticed the "time" field which refers to a very old date, but i don't know what does it mean.
It is kind of bad practice to enable Tls12 like this-
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
In future, if you'd need to use higher version of TLS, you'll have to update your code.
If you are using an older version of .NET, you can simply switch it higher version in which Tls12 is enabled by default.
For example, this simple change in your web.config will enable Tls12 automatically-
<httpRuntime targetFramework="4.6.1"/>
You can try the code below:
string url = ""; // url of the endpoint
WebClient client = new WebClient();
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
client.Encoding = Encoding.UTF8;
client.Headers.Add("content-type", "application/json"); // same as other parameters in the header
var data = client.DownloadString(url);
Figured it out. I needed to include the use of TLS1.2.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
(As a reference for others who have the same issue)this also might be the result of a Double Hop issue , where you should pass the credited user along(in the pool) to the passing server or from one Environment to the other , otherwise the user is set to "ANONYMOUS/USER" and you will get a "An existing connection was forcibly closed by the remote host." Error
i found same error, just mention
request.UserAgent = "anything u want";
I've seen a lot of question about this, and all points to me using ConfigureAwait(false), but even after doing so, it still doesn't returned any response. When I run the debugger, the code stops at the PostAsync and does not continue with my code. Am I doing something wrong here? Does it have to do with me calling an API via HTTPS?
Here's the code:
public async static Task<PaymentModel> AddAsync(Card card)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:", "hidden"))));
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
var cardJson = JsonConvert.SerializeObject(card);
var postRequest = new StringContent(cardJson, Encoding.UTF8, "application/json");
var request = await client.PostAsync(new Uri("https://sample-3rd-party-api/api/endpoint/here"), postRequest).ConfigureAwait(false);
var content = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
}
EDIT:
In response to the comments below, the code is contained from a method AddAsync(Card card) called from a button click with a handler:
public async void OnExecute(object sender, EventArgs args)
{
//some code here
payment = await PaymentModel.AddAsync(card).ConfigureAwait(false);
}
EDIT 2:
I tried pinging the API, but it returns a request timed out, but when I tried it using Postman, it's doing fine (the API is just a Sandbox which is open for all, so it's okay to share this):
EDIT 3:
I think the problem lies with where I don't have an SSL certificate to access the API. I have a PHP server that connects to the same API and I have to set SSL_VERIFYPEER to false just so I can access it (don't worry, I added a cacert now so its on true again). Can the same issue be happening here? If so, what can I do to create a valid certificate for my xamarin forms app
You can use this
var json = JsonConvert.SerializeObject(card);
using (var client = new HttpClient())
{
var t = await client.PostAsJsonAsync("https://sample-3rd-party-api/api/endpoint/here", json);
Response R =JsonConvert.DeserializeObject<Response>((JsonConvert.DeserializeObject(t.Content.ReadAsStringAsync().Result.ToString())).ToString());
}
What's most likely happening here is your OnExecute method has a return type of void instead of Task which prevents the UI thread from being able to await it. Try either changing that return type to Task or creating a new UI thread to perform this work. I wouldn't worry about the ping timing out as long as Postman works. Many public web servers disable their ping response.
Does it have to do with me calling an API via HTTPS?
As you are remaining in the same network and calling the same API from POSTMAN and .NET HTTP Client and only getting success with POSTMAN.So this issue gets cancelled.
Next
tried pinging the API, but it returns a request timed out
This is answered on top of mine.
Can you Please try setting the timeout option for HTTPClient while initializing.
client.Timeout = TimeSpan.FromSeconds(10);
and if still Problem persists please setup Fiddler and compare both the req sent from POstman and .NET client
So I think the problem is resolved now since I'm able to receive content from the request, what I did was simply follow the docs here: https://learn.microsoft.com/en-us/xamarin/cross-platform/app-fundamentals/transport-layer-security?tabs=windows
It looks like my settings are outdated in platform level.
Update the HttpClient implementation and SSL/TLS implementation
options to enable TLS 1.2 security.
Update the HttpClient Implementation option to enable TSL 1.2
security. (NSUrlSession (iOS 7.0+)
I was having the same issue and below trick fixed the issue.
Change your var request = await client.PostAsync(...); as below
var task = client.PostAsync(new Uri("https://sample-3rd-party-api/api/endpoint/here"), postRequest);
var request = task.GetAwaiter().GetResult();