HttpWebRequest Cipher Suites Selection - c#

When establishing an https connection via HttpWebRequest in a C# application where is the list of available ciphers suites that are provided in the SSL handshake stored on the server (2008R2)? Is it a registry setting or how is this determined?
The C# code is just a simple
HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(Uri);
WebReq.Method = "GET";
HttpWebResponse WebResp = (HttpWebResponse)WebReq.GetResponse();
The issue I am having is that when using IE from the server the list of ciphers provided is different to what is being provided by the C# application (As captured by wireshark.) As such the C# based connection if failing as the Server supports and wants to use TLS_RSA_WITH_RC4_128_SHA which is not being provided by C# app but is being provided by browser SSL handshake.
Using the same code on other servers shows that TLS_RSA_WITH_RC4_128_SHA is being offered in the SSL handshake by the C# app so it leads me to believe that there is something environmental on that particular server but where and how this is controlled is not something I have been able to find.
I would post images of the wireshark captures to show the difference between C# application and IE SSL handshake Client Hello Cipher suite list but I have low rep points.

Here is the registry path SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers

Related

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.

What is different in the .NET (c#) implementation of webclient versus java or firefox RESTClient?

I need to post JSON to a https endpoint using c# .
I am using System.Net.WebClient (or HttpWebRequest ).
When I post the JSON to the endpoint using JAVA or the firefox RESTClient everything works fine (from the same machine).
With Wireshark I can see that the receiving server RESETs the connection, resulting in this .NET exception:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
I don't use any proxy servers.
I have set the request timeout to -1 (and other values).
What can the .NET runtime be adding to (or removing from) the requests that the firefox RESTPlugin en JAVA are not ?
There must be a difference.
Fiddler shows me two http(s) requests with response status 200, but no data seems to be coming back (and Fiddler introduces a proxy...)
#Mason thanks for making me look once more at the fiddler data.
After setting the protocol to TLS1.2
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
It works.
I have seen posts that actual get an error message hinting at the minimal TLS support. But here I had to go through StackOverflow first.
Just the exercise of formulating the question and the first quick responders helpt me fix this quickly !

C#: Send XML over https/SSL3 with client/server certificates

I need to develop a C# client able to post XML docs into a SAP Bussiness Connector 4.6.
The Client:
.NET until 4.6, VS 2013 available, SO Win 8.1 Pro 64 bits.
The Server:
Windows 2000, with SAP Business Connector 4.6 (it's really equal to WebMethods 4.6), configured with https and Client certificate as authentication method. The server has a certificate that doesn't match the URL used in local development against it.
I'm trying several .NET methods and clients. I'm actually trying httpClient to connect with BC, without success.
The actual code is very similar to this:
try
{
System.Net.Http.HttpClient client;
//HttpClient uses the HttpMessageHandler pipeline for sending and receiving requests
//WebRequestHandler derives from HttpClientHandler but adds properties that generally only are available on full .NET
System.Net.Http.WebRequestHandler wrHandler = new System.Net.Http.WebRequestHandler();
System.Security.Cryptography.X509Certificates.X509Certificate x509cert = System.Security.Cryptography.X509Certificates.X509Certificate2.CreateFromCertFile("MyClientCertificate.crt");
wrHandler.ClientCertificates.Add(x509cert);
client = new System.Net.Http.HttpClient(wrHandler);
//THIS SKIPS SERVER CERTIFICATE
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
System.Net.Http.HttpRequestMessage request = new System.Net.Http.HttpRequestMessage();
request.Method = System.Net.Http.HttpMethod.Post;
request.RequestUri = new System.Uri("https://192.168.12.12:3333/invoke/wm.PartnerMgr.flows.UCLR.0000000001:ORDERS");
request.Content = new System.Net.Http.StringContent(CtrXMLToSendContent.Text, Encoding.UTF8, CtrClient1MediaType.Text);
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3 | System.Net.SecurityProtocolType.Tls;
Task<System.Net.Http.HttpResponseMessage> taskResponse = client.SendAsync(request);
if (taskResponse.Wait(-1)) //-1 = infinite
{
//Response received
System.Net.Http.HttpResponseMessage response = taskResponse.Result;
TextResponseData.Text = response.ToString();
Task<String> taskResponseStr = response.Content.ReadAsStringAsync();
taskResponseStr.Wait();
String responseContent = taskResponseStr.Result;
TextResponseContent.Text = responseContent;
}
else
{
TextClient1Infolog.AppendText("Wait ");
}
}
catch (Exception _e)
{
TextClient1Infolog.AppendText("Error :" + Environment.NewLine);
TextClient1Infolog.AppendText(_e.ToString());
}
When I launch it, it throws an exception, giving this error:
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.
There's no server response at all I can explore or debug. I think it's a problem of handshaking SSL protocol. I must insist: There's no response text from server, no 402 or 404 message errors at all: Nothing is received.
Firefox over that direction says no connection. IE, with TSL 1.0, 1.2 and 1.3 activated, says it cannot connect; activating SSL 3.0 and reopening page says there's a problem with server certificate (yes, because I'm accesing from local network, where certificate states for something like "this.server.com").
Fiddler4 doesn't say me much (and autocertificates appears to be confusing something), SoapUI appears to be not very useful here (haven't see way to set certificates and SSL handshakes).
Found a tool (TestSSLServer, link: http://www.bolet.org/TestSSLServer/) that gave me this info:
Supported versions: SSLv3 TLSv1.0 Deflate compression: no Supported
cipher suites (ORDER IS NOT SIGNIFICANT):
SSLv3
RSA_EXPORT_WITH_RC4_40_MD5
RSA_WITH_RC4_128_MD5
RSA_WITH_RC4_128_SHA
RSA_EXPORT_WITH_RC2_CBC_40_MD5
RSA_EXPORT_WITH_DES40_CBC_SHA
RSA_WITH_DES_CBC_SHA
RSA_WITH_3DES_EDE_CBC_SHA
(TLSv1.0: idem)
---------------------- Server certificate(s):
188659f61762af0de690bf5cb76a8554e7ff7f23: CN=my.server.com,
OU=Domain Control Validated
---------------------- Minimal encryption strength: weak encryption (40-bit) Achievable encryption strength: strong encryption
(96-bit or more) BEAST status: vulnerable CRIME status: protected
So I suppose I have to force SSL3 handshake (I think this is OK in my code), validate server certificate (too)... perhaps I must do something with client certificate.
Must I generate one for my client computer and load it? How can I generate one with one of the encriptions of the TestSSLServer's list? Wich file should be installed in what computer?
And I'm open to hear about other http clients (ServerXMLHTTP, etc) or tools able to test xml postings over xml. Any ideas about how to proceed for now?

WinForm Security Context

I have written a WinForms app that uploads addresses from a spreadsheet, and geocodes them using an external geocoding service. This all works fine on my local machine, but the time has come for it to be installed on other peoples computers for testing. The app no longer works now though, generating the below error:
System.Net.WebException: The remote server returned an error: (407) Proxy Authentication Required.
Having read a lot and chatted breifly to our network guys, it seems i need to establish the Security Context for the users account and work with this to correct the error.
Has anyone got any pointers about how I should be going about this?
Thanks in advance!
C
It depends on how your uploading the data. If your using a http request (as it looks like you are) it will look something like;
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("https://test.example.com/");
req.Method = "POST";
req.ContentType = "text/xml";
req.Credentials = new NetworkCredential("TESTACCOUNT", "P#ssword");
StreamWriter writer = new StreamWriter(req.GetRequestStream());
writer.Write(input);
writer.Close();
var rsp = req.GetResponse().GetResponseStream();

How do I determine the "strength" of an SSL connection

I have a rich client application that is connecting to a set of backing web services where the connection is secured by SSL. I need to determine the "strength" of the encryption being used for the actual SSL stream to display this information to the end user.
My understanding is that the client and server will negotiate a symmetric encryption method between them (SSL/TLS) with different levels of encryption (40,56,128,256). Is there any way I can detect which mode is being used from a HttpWebRequest/ServicePoint/other in C# code?
This expands upon #Alex's post, obviously add your own error handling
System.Net.Sockets.TcpClient TC = new System.Net.Sockets.TcpClient();
TC.Connect("mail.google.com", 443);
using (System.Net.Security.SslStream Ssl = new System.Net.Security.SslStream(TC.GetStream()))
{
Ssl.AuthenticateAsClient("mail.google.com");
Console.WriteLine(Ssl.CipherAlgorithm);
Console.WriteLine(Ssl.CipherStrength);
}
TC.Close();
I don't think you can access the SSL information from the web service directly, you'll have to use this helper code to talk to the host directly.
Since you have established an SslStream stream, you could use the following:
stream.CipherAlgorithm //to get the algorithm that is used
stream.CipherStrength //to get the strength of the cipher algorithm
You can find more information here.

Categories

Resources