Calling REST service with domain credentials from .NET 4.5 HttpClient - c#

I want to call a REST service that requires domain authentication from .NET 4.5. (Using Visual Studio 2012)
Searching Google leads to lots of links of people saying that HttpClient is now the way to do this.
However as far as I can tell there is no way to impersonate or attach credentials to HttpClient.
In addition, all the popular REST libraries seem to not be compatible with .NET 4.5 yet.
Over StackOverflow posts have suggested WebClient as a way around this, although this seems no longer available in .NET 4.5.
If I want to call a REST service with domain credentials from a .NET 4.5 client, what is the best method?

HttpClient in .NET 4.5 does support domain authentication. You need to insert a HttpClientHandler with the 'UseDefaultCredentials' setting set to true:
string searchResults = string.Empty;
try
{
HttpClientHandler handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
HttpClient client = new HttpClient(handler);
client.MaxResponseContentBufferSize = 100000;
string responseString = await client.GetStringAsync(RestServiceUrl);
searchResults = responseString;
}
catch (HttpRequestException e)
{
searchResults = e.Message;
}
Also it is worth noting that if you are building a Windows 8 application then you need to enable 'Enterprise Authentication' in the Package.appManifest:

Related

How can I disable the SSL Certificate check in the .NET Core http client?

I am trying to connect to a website via a proxy. This is happening in an AWS Lambda with .NET Core SDK using an http client. The call looks pretty much like this:
handler = new HttpClientHandler()
{
CookieContainer = cookieContainer,
Proxy = new WebProxy(
new Uri(Environment.GetEnvironmentVariable("proxyURL"))),
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator,
SslProtocols = SslProtocols.Tls12,
ClientCertificateOptions = ClientCertificateOption.Manual
};
using(var client = new HttpClient(handler))
{
var content = await client.GetAsync("https://my-website.com/");
}
I am not able to make the call to "https://my-website.com/". The call times out without an error message.
However I was able to access the website using Golang and resty in an AWS Lambda, skipping the TLS Check:
client := resty.New()
resp, err := client.
SetProxy(os.Getenv("proxyURL")).
SetRetryCount(3).SetTimeout(3*time.Second).SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
R().Get("https://my-website.com/")
My question is: how can I achieve the behaviour from my Golang Code in my .NET Core Code?
TL; DR: The problem lied not within certificate validation, but in a security group rule.
As Marc Gravell kindly pointed out, the DangerousAcceptAnyServerCertificateValidator already achieves the aim of ignoring certificate validation problems.
After deploying the same code in an EC2 instance in the same VPC and subnet I noticed, that the .NET Core code was making one more HTTP call than the Go code (Although I still do not understand why). The IP adress was not within the allowed IP range of outgoing traffic, thus blocking the request and causing a timeout of the HttpClient.

Different behaviour of HttpWebRequest/HttpClient in .NET Core and .NET Framework

I am writing a console application which uses SSO (Windows Authentication) for login into a third party web API. The code works with .NET Framework 4.6.1, but not with .NET Core 3.1.
During the tests I reduced the problem to the request to get the token from the identity provider and created two application, one with .NET Framework 4.6.1 and one with .NET Core 3.1 with exact the same code in Main:
string uri = "http://<server>/connection/single-sign-on/identity-providers/90c22f9b-0a9d-4474-87f4-b48eccbe3095?singleSignOnCapabilities=saml2Redirect";
try {
using (HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true })) {
var response = client.GetAsync(uri).Result;
Console.WriteLine(response.StatusCode);
if (response.IsSuccessStatusCode) {
// Get token from response header
// This works with .NET 4.6.1
}
}
}
catch {
// In .NET Core 3.1 I get an exception: The remote server returned an error: (401) Unauthorized.
}
Because of the comment from Camilo Terevinto, I changed the example to HttpClient with the same result.
Btw: Can experience the same behaviour with Windows Powershell and Powershell Core. It seems to be a general different implementation on Framework and Core.
In addition to reponse in comments I found different problem with HttpClient on .NET Core vs .NET Framework. If you have authorization defined in cookies in .NET Framework you have to do it with CookieContainer in HttpHandler:
var cookieContainer = new CookieContainer();
var handler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
Instead of doing it by HttpRequestMessage header:
var message = new HttpRequestMessage(HttpMethod.Post, <ip>);
message.Headers.Add("Cookie", <cookie>);
The second way only work in .NET Core and in .NET Framework I constantly received 401 Unathorized response.

System.Net.Http.HttpClient cannot connect to web API via NSwag client (Windows Universal Platform)

I wrote a simple .Net Core 3.0 API using Swashbuckle Swagger and generated an api client via NSwag Studio, then I put a generated Api Client to a .Net Standard 2.0 project.
I have a Universal Windows Platform application, which is meant to be connecting to the Web Api and send/receive data etc.
I put a simple code in MainPage.xaml.cs class with System.Net.Http.HttpClient inside
public MainPage()
{
this.InitializeComponent();
var httpClient = new HttpClient();
var apiClient = new ApiClient(ProjectConstants.API_URL, httpClient);
var deviceService = new DeviceService(apiClient);
}
When API is called later in deviceService my program throws an exception on SendAsync method in generated Api Client
"An error occurred while sending the request."
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
I tested API Client and HttpClient on .Net Core console app and it worked fine.
I read about Http Client for UWP https://learn.microsoft.com/en-us/windows/uwp/networking/httpclient but it seems UWP should support System.Net.Http.HttpClient too, which I would love to stick to.
Is this a Universal Windows Platform bug or do I forgot about adding something necessary to a project?
The System.Net.Http.HttpClient API can be used across platforms, but we do recommend using APIs from Windows.Web.Http.HttpClient NameSpace which is easy to use. So you could use Windows.Web.Http.HttpClient API in UWP,and use GetAsync(PostAsync) to send a request, for example:
Uri requestUri = new Uri("http://www.contoso.com");
Windows.Web.Http.HttpClient httpClient = new Windows.Web.Http.HttpClient();
Windows.Web.Http.HttpResponseMessage httpResponse = new Windows.Web.Http.HttpResponseMessage();
httpResponse = await httpClient.GetAsync(requestUri);
For more information about the differences between Windows.Web.Http and System.Net.Http.HttpClient, you can refer to this article
Does it have the same api as the portable HttpClient?

HttpWebRequest call to GetResponse, fails using .net 4.5 but passes using net 4.6

We've just launched a new service, which we're having trouble connecting to from a very basic c# console app when targeting the .Net 4.5 framework.
We first found the problem in an ASP MVC site, but have broken it down into the simplest of simple console apps to help isolate the problem
Code snippet (there isn't anything else):
string myURL = #"https://<myurl>.com/<myurl>";
using (var httpClient = new HttpClient())
{
var request = (HttpWebRequest)WebRequest.Create(myURL);
request.Method = "GET";
request.ContentLength = 0;
request.ContentType = "text/xml";
using (var response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode != HttpStatusCode.OK)
{
...
}
else
{
...
}
}
}
What happens
Web Exception - Underlying Connection Closed.
Thrown on the call to GetRequest
Service Information
HTTPS service
SHA-256.
Other differences
Examining the request variable in the intermediate window before it is sent shows no difference.
Examining the request variable after the call has been attempted shows one difference - System.Net.HttpWebRequest.Pipelined is true in the successful attempt, false in the failed attempt.
Setup Essentials
Load balancer, balancing between two API's hosted in IIS.
Has it ever worked?
Yes!
If the URL is dropped into a browser - it works.
If I recompile this code targeting the .net 4.6 framework - it works.
If I connect directly to the site in IIS (over http) it works.
What am I asking
What could be causing this problem?
Have you seen similar and have suggestions for possible remedies?
What further steps would you take to help debug / solve the issue.
How would changing .Net framework version to 4.6 affect the HttpClient or the HttpWebRequest?"
Thanks,
Al.
This is usually caused by the server using TLS v1.2. I think Net4.5 defaults to TLS v1.1, so you must add this to your code:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

Server Name Indication from C#

As far as I can tell, there seems to be a big limitation in .NET in that there is no way using C# and .NET to make an TLS connection that uses Server Name Indication (SNI). Have I missed something or is my understanding correct?
Does anybody know if and how I could make an SNI connection using OpenSSL.NET, libcurl.NET or some other 3rd party .NET library? Some sample code would be very much appreciated.
In my .Net 4.5 project the following fails for a server using SNI:
var url = "https://www.somesite.com";
System.Net.WebClient client = new System.Net.WebClient();
client.Encoding = Encoding.UTF8;
var data = client.DownloadString(url);
But it works if explicitly specifying TLS1.2 by prefixing it with:
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
The same applies to webrequest:
WebRequest request = WebRequest.Create("https://www.somesite.com");
and HttpRequestMessage:
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://www.google.com");
They all need the protocol explicitly set to TLS 1.2 to work with an SNI server (this may have changed in newer .Net versions)
This is a fairly old post but still this answer might help some people, at least it cost me some days.
.NET Framework does support the Server Name Indication by default. (Tested on 4.5.1 but I guess it's same at least for .NET 4.5+)
A short example:
HttpResponseMessage response;
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://www.google.com");
var handler = new HttpClientHandler
{
CookieContainer = new CookieContainer()
};
using (var client = new HttpClient(handler))
{
response = client.SendAsync(httpRequestMessage).Result;
}
This is a very standard way to create a GET request within C#. You will see, this example does run using, in my case, TLS 1.2 with SNI. If you use Wireshark to see the actual packages which are sent, you will see a Client Hello with the Server Name Indication set to www.google.com.
An issue we ran into: The SNI tag is set by the .NET Framework (or Schannel by Windows, not sure) based on the URL passed in the constructor of HttpRequestMessage. If you know initialize the request based on some URL (for example https://www.google.com) and later on you switch the RequestUri OR the Host header, the SNI tag will still be created based on the original url URL. This might be the case for example if you pass through a request and you remap all original headers to the newly created request.

Categories