Authorization error trying to connect to Dynamics CRM using OAuth - c#

I'm trying to connect to a remote Dynamics CRM instance and getting this exception on the ServiceClient constructor:
Failed to connect to Dataverse
Inner Exception 1: One or more errors occurred.
Inner exception 2: Need a non-empty authority
Parameter name: Authority
Key here is that it works fine from my dev machine--the error only occurs when I move the code to another server.
Here's the code:
string crmConnectionString =
$"AuthType=OAuth;Username=user#contoso.com;Password=whatever;Url=my-app.crm.dynamics.com;LoginPrompt=Never";
using (ServiceClient service = new ServiceClient(crmConnectionString)) // throws here
I used Wireshark to sniff the data and noticed the working server is sending the client hello using TLS v1.2, whereas the failing server is sending a slightly shorter (fewer bytes) hello using TLS v1. Could the issue be related to this and, if so, how do I fix it?

I have confirmed that TLS 1.2 is indeed required when communicating with online Dynamics 365. The solution in my case was to add this line directly above the constructor:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
This forces the protocol to TLS 1.2 and allows the code to work on both servers.
Note that there are probably better ways to solve this, such as upgrading your OS to get the newer TLS. That way your code won't be stuck on TLS 1.2 when newer versions become available. But the code addition is a potentially quick way forward for those who need it.
More info here and here.

Related

{System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.} C#

I have a C# code which uses HttpClient.SendAsync method to make an API call to azure maps API. When I run this code, I'm getting following error.
{System.Net.Sockets.SocketException (10054): An existing connection
was forcibly closed by the remote host.}
I searched in multiple websites for solution and tried the following piece of code just before making API request
System.Net.ServicePointManager.SecurityProtocol |= (System.Net.SecurityProtocolType)768 | (System.Net.SecurityProtocolType)3072;
But there's no use. I'm still getting the error.
I tried to set security protocol in different ways as follows:
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.SystemDefault;
Even these pieces of code didn't help. I'm still getting same error.
I set those security protocol types using powershell like
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
I tried powershell Invoke-RestMethod command as well. I'm getting similar error:
Invoke-RestMethod : The underlying connection was closed: An
unexpected error occurred on a send
I didn't understand
What's meant by System.Net.ServicePointManager.SecurityProtocol and what is the use of it
What's meant by System.Net.SecurityProtocolType, what's the use of it, what type to choose for my requirement
or am I getting this error because of different problem
Can someone please help?
What's meant by System.Net.ServicePointManager.SecurityProtocol and what is the use of it
System.Net.ServicePointManager.SecurityProtocol Gets or sets the security protocol used by the ServicePoint objects managed by the ServicePointManager.
For new connections, this property specifies which version of the Secure Sockets Layer (SSL) or Transport Layer Security (TLS) protocol to use; existing connections are not altered.
What's meant by System.Net.SecurityProtocolType
It Specifies the security protocols which are supported by the Schannel security package.
This enumeration defines the range of values that can be used to specify the transport security protocol to utilise.
This error usually indicates that the remote side of the connection has been closed (normally by sending a TCP/IP 'RST' packet). If you're using a third-party application, the following are the most likely causes:
You are sending malformed data to the application (which could include sending an HTTPS request to an HTTP server)
For some reason, the network connectivity between the client and the server is failing.
You caused the third-party application to crash by triggering a bug in it.
To narrow down the problem, use Wireshark to examine exactly what is going on on the wire. Also this may be due to Security Protocol support issue, try upgrading to the latest .NET framework Version as it is a connection issue.

Self hosted GRPC server

I am attempting to transition away from WCF to move our codebase across to .NET Core. Our services are all hosted as Windows services at present so am trying to self-host the gRPC service as well (rather than building AspNetCore applications). I have successfully built a server using Grpc.Core.Server, and the client side as well with Grpc.Net.Client.GrpcChannel, see the code snippets below for reference.
Server:
var builder = ServerServiceDefinition.CreateBuilder();
// Binder is a small class ripped from the CodeFirst example
var binder = new Binder();
binder.Bind(builder, serviceType, service: serv);
var serverServiceDefinition = builder.Build();
var server = new Grpc.Core.Server
{
Services = { serverServiceDefinition },
Ports = { new ServerPort(host, port, ServerCredentials.Insecure) }
};
server.Start();
Client:
var channel = GrpcChannel.ForAddress(Uri, new GrpcChannelOptions()
{
//HttpHandler = new GrpcWebHandler(new System.Net.Http.HttpClientHandler())
});
var service = channel.CreateGrpcService<TService>();
However because our applications are still running in .Net Framework 4.8 I get the runtime exception when testing out this code:
System.PlatformNotSupportedException : gRPC requires extra configuration on .NET implementations that don't support gRPC over HTTP/2. An HTTP provider must be specified using GrpcChannelOptions.HttpHandler.The configured HTTP provider must either support HTTP/2 or be configured to use gRPC-Web. See https://aka.ms/aspnet/grpc/netstandard for details.
That leads me to add in the Grpc.Net.Client.Web.GrpcWebHandler on the client side to switch over to Grpc-web as per the link in the error.
However, I am now struggling to do the equivalent for the server to support Grpc-web. The guide here suggests to either (1) use Grpc.AspNetCore.Web or (2) use "Envoy proxy" to get the server supporting it. The problem with (1) is that I'm not using AspNetCore so I don't think this solution is appropriate, and I can't find any lightweight/easy way to do (2) in a simple C# solution.
Without the server-side support added, I get this exception:
Grpc.Core.RpcException : Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request. WebException: The server committed a protocol violation. Section=ResponseStatusLine", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request.
Which I assume is obviously related to the fact the server isn't supporting the Grpc-web requests. So I am at a bit of a dead end with regards to this now. I feel I need to work out how to self-host AspNetCore servers and move to that instead of Grpc.Core.Server, which will open up option (1), but I am finding little to no evidence that is actually possible.
So I guess my main question is: Is there any way to support Grpc-web clients in a server hosted via Grpc.Core.Server?
And if the answer is no --> How can I self-host a GRPC server that will support Grpc-web clients?
As per this getting started guide I have discovered protobuf-net.Grpc.Native which appears to solve the problem I have at the moment. I also discovered I was missing a default constructor for my [DataContract], which I think was unrelated to the errors I was receiving but may have been contributing.

.net httpclient on Azure w/ TLS/SNI: The request was aborted: Could not create SSL/TLS secure channel

Similar questions have been asked multiple times before - but before flagging this one as duplicate please read on. Most of these questions are very old. I have worked through a lot of questions and answers and did not find a suitable solution.
We have an Azure Cloud Service project in .net 4.5. It connects to dozens of our customers' APIs (not necessarily cloud hosted) without any problems, but a single API fails with this error message:
The request was aborted: Could not create SSL/TLS secure channel
What am I missing here?
This is the code (slightly condensed) that I am using to connect to the API (this runs per API, so the base URL does not change):
ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls |
SecurityProtocolType.Ssl3;
ApiClient = HttpClientFactory.Create();
ApiClient.DefaultRequestHeaders.Authorization = null;
ApiClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Basic {passwordToken}");
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
SemaphoreSlim throttler = new SemaphoreSlim(initialCount: 50);
var sp = ServicePointManager.FindServicePoint(new Uri(baseUrl));
sp.SetTcpKeepAlive(true, 30000, 30000);
foreach (var request in urls)
{
Result = new HttpResponseMessage();
Result = await ApiClient.GetAsync(url);
...
}
This is what makes it hard to debug:
This problem only occurs in production, i.e. when running as an Azure Cloud Service. Not when debugging locally.
It only occurs with requests sent through HttpClient. Not with WebClient.
Further research (comparing the APIs) revealed that this API is the only one that has enabled SNI and ONLY supports TLS1.2.
Suggestions considered from other questions/answers regarding SNI in .net Framework:
To prevent misunderstandings: This is about the cloud service connecting to an API, not about a connection that is being made to the cloud service.
The HttpClient instance is being reused for all requests to a single API. (This is important as this answer suggests that the SNI tag will be created with the domain HttpClient has been initialized with). I have also tried configuring TLS after the Factory instantiated the HttpClient. No change.
The certificates are valid of course. No self-signed certificates but regular trusted ones off the shelf. Opening the API in any browser also works like a charm.
TLS1.2 is not enabled by default in .net framework 4.5, but the line ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12should actually enable it. Is there anything wrong with the way I am doing it?
Calling the API in curl (WSL and Azure remote bash) with curl --user 'user:pwd' https://myurl also works perfectly and returns the expected data.
Testing tls1.2 with openssl like openssl s_client -connect hostname:443 -tls1_2 does not reveal any issues. The chain is displayed correctly, and a TLSv1.2 session is confirmed. Testing the server's SNI feature with openssl with openssl s_client -connect host:443 -tls1_2 -servername host -tlsextdebug -msg reveals SNI support by returning TLS server extension "server name" (id=0), len=0 I get the same certificate if I provide a completely different fantasy hostname though.
I captured the TLS/SNI handshake when debugging locally (see screenshot below). No issues. My ability to debug ends with the cloud service. I would love to see the handshake between the cloud service and the API in WireShark, but I don't know of any option to analyze network traffic at that layer on an Azure cloud service. But if anyone knows how to capture the handshake process, I'd appreciate some hints.
The server selects ECDHE-RSA-AES256-GCM-SHA384 as a cipher suite during the handshake with openssl which is pretty much default for TLS1.2. I don't have access to the cloud services list of cipher suites that it would provide at Client Hello - any idea how to find out?
I don't have any proof that SNI is actually causing the problem, but this is the only difference between this API and dozens of others I can spot.
Stack trace:
System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel. at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) --- End of inner exception stack trace --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at WCFServiceWebRole1.Controllers... in [the line calling GetAsync()]
I ended up re-creating a simple API on one of my servers and configured the software in such a way to send its requests there. That way I could capture the TLS handshake and analyze it in Wireshark. These are the supported cipher suites (client side, which is Azure cloud service):
And these are the cipher suites supported by the API which is not working:
I would assume that there should be a match so that server and client can agree on one. However, I cannot find a match... guess that is what is causing the problem. In fact, the list of supported cipher suites is much longer in a local debugging session - and there is at least one match which explains why it works locally.

SSL Error "The message received was unexpected or badly formatted" for a .NET application on one specific machine only

I have a .NET Core 3.1 C# application which is calling an API via HTTPS (and presenting its public key as part of getting the token as that certificate is later used to decrypt information sent back separately). On just about all our machines, it is working, but on one Windows 8.1 machine, we get the following series of exceptions when we try to initially connect for an authentication token:
The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> System.ComponentModel.Win32Exception (0x80090326): The message received was unexpected or badly formatted.
The exception is thrown from System.Net.Http.HttpClient.FinishSendAsyncBuffered so I suspect it is happening at the HTTPS level and our certificate stuff is not really relevant here anyway.
Our code to get the token looks like this:
The constructor for the auth service:
public XXXXAuthService(IXXDbService dbService, XXXXApiConfig config)
{
_dbService = dbService;
_config = config;
// try forcing TLS1.2 for SSL connection exceptions thrown in some operating environments
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
_httpClient = new HttpClient {BaseAddress = new Uri(config.BaseUrl)};
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
Code to get the auth token:
private async Task<string> GetXXXXBearerToken(string userId, DateTime creationTime)
{
var token = await GenerateProviderJwtForXXXX(userId, creationTime);
var kvp = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"),
new KeyValuePair<string, string>("subject_token", token),
new KeyValuePair<string, string>("subject_token_type", "urn:ietf:params:oauth:token-type:jwt")
};
var data = new FormUrlEncodedContent(kvp);
var publicKey = await GetXXXXPublicKey();
_httpClient.DefaultRequestHeaders.Remove("X-XXXX-Public-Cert");
_httpClient.DefaultRequestHeaders.Add("X-XXXX-Public-Cert", publicKey);
var response = await _httpClient.PostAsync("Identity/token", data);
if (!response.IsSuccessStatusCode)
throw new Exception("XXXX Token Server Error: " + response.ReasonPhrase);
var result = await response.Content.ReadAsStringAsync();
var authResponse = JsonConvert.DeserializeObject<OAuthResponse>(result);
if (!string.IsNullOrEmpty(authResponse.access_token))
return authResponse.access_token;
System.Diagnostics.Trace.WriteLine("Token Exchange Result: " + result);
if (!string.IsNullOrEmpty(authResponse.error))
{
var outcome = new XXX.XXXX.Model.OperationOutcome();
outcome.Issue.Add(new XXX.XXXX.Model.OperationOutcome.IssueComponent()
{
//some code to throw an error is here
}
throw new XXX.XXXX.Rest.XXXXOperationException("Bearer Token Exchange failed", response.StatusCode);
}
Unfortunately none of the existing questions/advice anywhere on Stack Overflow, or the rest of the web, for this particular error seems to have helped. They are primarily about version discrepancies between client and server which seems not to be the case here as I am forcing TLS 1.2 (which is active and enabled on the failing machine).
Interestingly, I can visit the server URL in a browser via HTTPS just fine, which suggests there is something about my code that is the problem rather than the machine, but it works everywhere else.
I have confirmed that:
The certificate I am using to authenticate the connection on the machine is valid and has a chain of trust (though as above I don't think we are getting that far as the TLS connection itself is failing)
The server we are calling supports TLS 1.2 (by forcing it)
I can get to the website for the URL independently via the browser
Is there something I need to do either in the code or on the machine to get this call to work everywhere?
Things I have tried to resolve the issue
Installing all Windows 8.1 updates to present day
Forcing TLS 1.2 in the code (see above code sample)
Limiting VM to TLS 1.2 only
I might be able to at least point you in the right direction…
Same Symptoms
I had a .NET Core 3.1 web app running on IIS (Windows Server 2012 R2) that got the exact same error and stacktrace when it tried to connect to another server using TLS 1.2. I also had the symptom where I could connect with the browser (Chrome), but not with the app. (Would have been interesting to see if Internet Explorer browser worked though.)
Root Cause
The TLS handshake was failing because the two servers were unable to agree on a common cipher suite. (Using Wireshark, I discovered that when my app tried to connect it provided a more limited set of cipher suites than when the Chrome browser made the call.)
Solution
In my case, I used IIS Crypto (a small free tool: https://www.nartac.com/Products/IISCrypto/) to enable additional cipher suites on my web app's server. I downloaded and ran IIS Crypto, checkmarked additional cipher suites on its Cipher Suites tab, and then restarted the machine.
One of the new cipher suites worked with my app and the destination server, so the TLS handshake was successful and the error was resolved.
One quick caveat: Some cipher suites are more secure than others, so you'll want to read up on best practices.
Addendum
If you want to further diagnose the failure, I'd recommend installing Wireshark (another free tool: https://www.wireshark.org/#download) on the machine with your .NET Core app. If a TLS Handshake Failure is the issue, you will see a message like: Alert (Level: Fatal, Description: Handshake Failure)
This primer on wireshark output helped me:
https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/
I faced a simular issue, and in order to help others here's what I concluded:
Sucessfully executing this code doesn't mean that your application supports the specified protocol version, and the "SSL Error" can still occur later on when trying to establish a connection:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
In my case I was trying to force Tls13 and found out that my app configuration didn't actually support it:
net core 3.0 running on a Windows Server Datacenter 2019, version 1809
So I had to change my configuration to the following which provides support for the protocol version I needed:
Net framework 5.0 on a Windows Server Datacenter 2022, OS build 20348.288
I was trying to connect to an endpoint that suddenly dropped Tls 1.2 support (not sure why) and from then on only accepted Tls 1.3.

Getting server certificate is not configured properly with HTTP.SYS when doing load test

I've a middle tier written in C# which consume a Soap endpoint provided by a client.
TLS 1.2 is mandatory , So I've this line in my code
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Also I've a P12 file which i read by this way
DeviceClient.ClientCredentials.ClientCertificate.Certificate = new X509Certificate2(ConfigurationManager.AppSettings["serverpath"].ToString(),ConfigurationManager.AppSettings["pwd"].ToString());
I'm able to get a response using sequential call. But when simulating a load test using soap ui, for some requests, I get the following error message:
Server certificate is not configured properly with HTTP.SYS.This could also be caused by a mismatch of the security binding between the client and the server.
Any ideas?
Regards

Categories

Resources