Connection issue with TLS 1.1 and 1.2 - Azure web app - c#

In my application I am calling an API written in WebAPI and hosted in a PaaS environment in azure from another WebAPI method (ideally an internal service call), say MethodA in WebApp_A is calling MethodB in WebApp_B. But i am getting the mentioned error if the TLS settings of WebApp_B is either 1.1 or 1.2 ( it works with 1.0).
"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host."
I have similar WebApp (WebApp_C) which doesn't have the error with TLS. Below is the code that we use to call the MethodB in WebApp_B from WebApp_A
public async Task<ServiceResponse> CreateDialog(RequestObject requestObject)
{
ServiceResponse serviceResponse = new ServiceResponse();
try
{
using (var client = new HttpClient())
{
SessionTokenBo jwtData = new SessionTokenBo();
Logger = _logger;
jwtData = GetInternalToken(InternalCallTypes.Utilities.ToString(), int.Parse(ServiceConfiguration.TokenExpiryWindow), int.Parse(ServiceConfiguration.InternalTokenExpiryWindow));
if (null != jwtData)
{
client.BaseAddress = new Uri("URI");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtData.Token); HttpResponseMessage response = await client.PostAsJsonAsync("service/methodB", requestObject);
if (response.IsSuccessStatusCode)
{
var data = response.Content.ReadAsStringAsync().Result;
serviceResponse = JsonConvert.DeserializeObject<ServiceResponse>(data);
}
}
}
}
catch (Exception ex)
{
throw
}
return serviceResponse;
}
If i give Security protocol like this , it will work
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Also i tried from postman with the request and it is not failing. So now am confused because if from postman its working then ideally its not the WebApp setup issue

TLS 1.0 is no longer the default. Add this line before making the request:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Have a Reference to this issue.
Update:
but my doubt is why the same code is not failing for other web apps ?
Found the issue, it was due the target framework was 4.5.2 in webconfig.
<compilation targetFramework="4.7.2"></compilation>
<httpRuntime targetFramework="4.7.2" />

Related

Azure function Error : received an unexpected eof or 0 bytes from the transport stream

I have a requirement where I am calling an API (programmatically PUT Method) from another API.
Both of the APIs are hosted as Azure Function App.
The request has nearly 600 rows.
The below method call is throwing the error: received an unexpected EOF or 0 bytes from the transport stream
If I send a request say 100-150 rows, it processes successfully.
I think that it is nothing to do with the code, it is related to the Azure Function app.
Please let me know if I need to add any configuration to the Azure Function app.
Thanks in Advance.
public async Task<List<CarPricing>> TestMethod(CarPricingModel request, string resourcePath,string token)
{
try
{
using var stream = StreamUtility.GenerateStreamFromString(JsonConvert.SerializeObject(request));
using var data= new StreamContent(stream);
data.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var queryParams = new Dictionary<string, string>()
{
{"id", "XXXXXXXXXXXXXXXXXXXXXX" }
};
var relativeUrl = QueryHelpers.AddQueryString(resourcePath, queryParams);
using var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Put,
Content = content,
RequestUri = new Uri(relativeUrl, UriKind.Relative)
};
var httpResponseMessage = await _httpClient.SendAsync(requestMessage);
httpStatusCode = httpResponseMessage.StatusCode;
var httpResponse = await httpResponseMessage.Content.ReadAsStreamAsync();
using var responseContent = new JsonTextReader(new StreamReader(httpResponse));
var response = new JsonSerializer().Deserialize<List<CarPricing>>(responseContent);
return response;
}
catch (Exception ex)
{
_log.LogError("API error {err_msg}",ex.Message);
throw;
}
}
Check the below steps that might help to fix the issue:
received an unexpected eof or 0 bytes from the transport stream
This error generally occurs during the HTTP Calls of .NET Core Applications.
TLS/SSL binding is supported in the Azure Function App. You can bind it through Azure Portal and using the Code.
If you’re using the HTTPS Protocol, apply this SSL Call before the request made as mentioned in the comments:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
or
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Ssl3| SecurityProtocolType.Tls| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12
but the above error might come for many causes such as:
Client IP may be restricted, which you can add in Access Restrictions of the Function app API.
Typo Mistake or Incorrect URL of the API that is called programmatically from another Azure Function API.
Refer to this MS Doc for using the TLS/SSL Certificate Programmatically and SO Thread that shows how to use the TLS/SSL Certificate in Azure Function App.

C# HttpClient cannot send authorization header to ASP.NET Core Web API

I am trying to use C# HttpClient from ASP.NET MVC to make a request to an API. My API is running on .NET 6.0.
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"{token}");
var serialized = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync(urlToSend, serialized);
Here is my code. I tried all the possibilities I saw on google. But when sending request, I can't send Authorization header.
I can send it with Postman.
Here is my API code:
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IResult))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(IResult))]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword([FromBody] ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return GetResponseOnlyResult(await Mediator.Send(changePasswordCommand));
}
Note: In my _url, I use http, not https.
I'm not sure but maybe the [AllowAnonymous]attribute remove the Authorization header from request just because it does not make sense if no authorization is needed.
Have you checked if the sent request contains the header using a tool like fiddler ?
I solved the problem by changing my base url from HTTP to HTTPS.
I tried with Fiddler and I got the same problem when I request to HTTP.
So thanks to #olivier-duhart .
To add to the accepted answer, the problem gets solved by changing from HTTP to HTTPS is due to the fact that, the Authorization header gets stripped during redirects.
This behavior is for security concerns and is by design, as mentioned in the github discussion here.
The same behavior may not be seen when using Postman vs HttpClient for example, is due to the way that different clients, have differing mechanisms, by which the subsequent requests (following a response status 30X) to the redirect location are handled.
Also a great answer elsewhere on stackoverflow : Authorization header is lost on redirect
Please review this link. Allow Anonymous will ignore the authentication header
https://github.com/dotnet/aspnetcore/issues/30546
I tried with the code. It seems working fine for me. Here is my code of console app
try
{
ChangePasswordCommand passobj = new ChangePasswordCommand() { password = "new password"};
string _url = "https://localhost:44395/api/Values/";
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"MYTOKEN");
var serialized = new StringContent(JsonConvert.SerializeObject(passobj), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync("changeuserpassword", serialized);
}
catch (Exception ex) {
}
And here is controler Api
[AllowAnonymous]
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword(ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return Ok();
}

HTTPClient to API, Certificate and SSL errors

I've created a ASP.Net Core 2.1 web application which gathers data from two sources. One is a SQL-database which supplies data via Entity Framework to my application. This one works fine. The other source is a REST API. I'm having troubles connecting to this.
I'm calling this Task which should return a user via his/hers e-mail address:
public async Task<PersonsModel> ReturnPersonByEmail(string email)
{
const string apiKey = "xxx";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://xx.xxx.xxxx.xx:xxx/xxxx/xxx/xx/xx/person/?email={email}");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("x-api-key", "123456");
var url = new Uri(client.BaseAddress.ToString());
string json = "";
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var response = await client.GetAsync(url);
using (var content = response.Content)
{
json = await content.ReadAsStringAsync();
}
}
catch (Exception e)
{
var exception = e;
}
return JsonConvert.DeserializeObject<PersonsModel>(json);
}
}
}
When I try calling it via Postman client.GetAsync(url) always returns an exception:
Message = "The remote certificate is invalid according to the validation procedure."
Message = "The SSL connection could not be established, see inner exception."
I tried adding the following codesegment to launchSettings.json(as per a reply to a similar question posted here: HttpClient client.GetAsync works in full .Net framework but not in .Net core 2.1? ) "DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER": "0" and then I get another error:
Message = "Error 12175 calling WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, 'A security error has occurred'."
If you have any idea on what might cause this problem I would be very grateful for any help. Thanks!

Bad Request : Sending request to ASP.NET WebAPI from android

I have tried to look for the solution for this with no success so far,
I am trying to call my ASP.NET WEB API (localhost:port) from Xamarin.Android (MainActivity).
I checked the API properly in Postman and it works as shown in the following screenshot
My code in Xamarin MainActivity is the following
try
{
using (var c = new HttpClient())
{
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync(new Uri("http://10.0.2.2:57348/api/remote"));
if (response.IsSuccessStatusCode)
{
Log.Info("myApp", "SUCCESS");
}
else
{
Log.Info("myApp", "ERROR: " + response.StatusCode.ToString());
}
}
}
catch (Exception X)
{
Log.Info("myApp", X.Message);
return X.Message;
}
I believe that 10.0.2.2 is to connect to the localhost from emulator -
When I run the code I get the error status as BadRequest
I also tried something like the following
try
{
Uri uri = new Uri("http://10.0.2.2:57348/api/remote");
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Method = "GET";
using (WebResponse response = await request.GetResponseAsync())
{
using (Stream stream = response.GetResponseStream())
{
Log.Info("myApp", "Success");
}
}
}
catch (Exception X)
{
Log.Info("myApp", X.Message);
}
I get 400 Bad Request
400 Bad Request means I am doing something wrong as assuming that my code can connect to the API but the server is considering API Call as invalid?
Just in case if anyone wants to know the code in my API, its the following
public class remoteController : ApiController
{
// GET: api/remote
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
Anyone has any idea about this, I have been trying different things for hours with no luck.
Also just to add, I tried 'http://10.0.2.2:57348/api/remote' in my Android Emulator's Chrome and I still get Bad Request response as shown in the following screenshot
but trying the same on my machine (browser) or Postman works fine using localhost
Please help
UPDATE:
Tried enabling External request on IIS Express using this http://www.lakshmikanth.com/enable-external-request-on-iis-express/
No luck,
The request is "bad" because the host header (in the request) is your 10.x.x.x. IP, and not localhost, which IIS Express won't accept.
We have an extension called "Conveyor", it's free and without configuration changes it opens up IIS Express to other machines on the network.
https://marketplace.visualstudio.com/items?itemName=vs-publisher-1448185.ConveyorbyKeyoti#overview
I think it is because of cross origin error add this in startup.cs ( in configure method)
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());

HttpClient failing in accessing simple website

Here is my code
internal static void ValidateUrl(string url)
{
Uri validUri;
if(Uri.TryCreate(url,UriKind.Absolute,out validUri))
{
using (HttpClient client = new HttpClient())
{
try
{
HttpResponseMessage response = client.Get(url);
response.EnsureStatusIsSuccessful();
}
catch (Exception ex)
{
//exception handler goes here
}
}
}
}
This code when i run it produces this result.
ProxyAuthenticationRequired (407) is not one of the following:
OK (200), Created (201), Accepted (202), NonAuthoritativeInformation
(203), NoContent (204), ResetContent (205), PartialContent (206).
All i want to do is make this code validate whether a given website is up and running.
Any ideas?
This basically means exactly what it says: That you are trying to access the service via a proxy that you are not authenticated to use.
I guess that means your server was reached from the Web Service, but that it was not permitted to access the URL it tried to reach, since it tried to access it through a proxy it was not authenticated for.
It's what EnsureStatusIsSuccessful() does, it throws an exception if status code (returned from web server) is not one of that ones.
What you can do, to simply check without throwing an exception is to use IsSuccessStatusCode property. Like this:
HttpResponseMessage response = client.Get(url);
bool isValidAndAccessible = response.IsSuccessStatusCode;
Please note that it simply checks if StatusCode is within the success range.
In your case status code (407) means that you're accessing that web site through a proxy that requires authentication then request failed. You can do following:
Provide settings for Proxy (in case defaults one doesn't work) with WebProxy class.
Do not download page but just try to ping web server. You won't know if it's a web page or not but you'll be sure it's accessible and it's a valid URL. If applicable or not depends on context but it may be useful if HTTP requests fails.
Example from MSDN using WebProxy with WebRequest (base class for HttpWebRequest):
var request = WebRequest.Create("http://www.contoso.com");
request.Proxy = new WebProxy("http://proxyserver:80/",true);
var response = (HttpWebResponse)request.GetResponse();
int statusCode = (int)response.StatusCode;
bool isValidAndAccessible = statusCode >= 200 && statusCode <= 299;
You are invoking EnsureStatusIsSuccessful() which rightfully complains that the request was not successful because there's a proxy server between you and the host which requires authentication.
If you are on framework 4.5, I've included a slightly enhanced version below.
internal static async Task<bool> ValidateUrl(string url)
{
Uri validUri;
if(Uri.TryCreate(url,UriKind.Absolute,out validUri))
{
var client = new HttpClient();
var response = await client.GetAsync(validUri, HttpCompletionOption.ResponseHeadersRead);
return response.IsSuccessStatusCode;
}
return false;
}

Categories

Resources