The SSL connection could not be established - c#

I am using a third party library (Splunk c# SDK ) in my ASP.NET core application. I am trying to connect to my localhost Splunk service via this SDK, but I get an exception saying:
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
And The inner exception says:
The remote certificate is invalid according to the validation procedure.
This SDK uses HTTP client under the hood, but I don't have access to this object to configure HttpClientHandler.
All my search on google ends up using ServicePointManager to bypass the SSL validation, but this solution doesn't work in Asp.Net core.
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Is there any way to bypass this validation in asp.Net core?

Yes, you can Bypass the certificate using below code...
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
// Pass the handler to httpclient(from you are calling api)
HttpClient client = new HttpClient(clientHandler);

As I worked with the identity server (.net core) and a web api (.net core) on my developer machine, I realized, that I need to trust the ssl certification of localhost. That command does the job for me:
dotnet dev-certs https --trust

If you are adding an IHttpClient and injecting through DI, u can add the configuration on the Startup.cs class.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("yourServerName").ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; }
});
}
And then call it from your dependency injected class.
public class MyServiceClass
{
private readonly IHttpClientFactory _clientFactory;
public MyServiceClass (IConfiguration configuration, IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<int> DoSomething()
{
var url = "yoururl.com";
var client = _clientFactory.CreateClient("yourServerName");
var result = await client.GetAsync(url);
}

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) =>
{
// local dev, just approve all certs
if (development) return true;
return errors == SslPolicyErrors.None ;
};
This blog helped me
https://www.khalidabuhakmeh.com/validate-ssl-certificate-with-servicepointmanager

This worked for me,
Create a Splunk.Client.Context by providing custom HttpClientHandler, that will bypass SSL invalid cert errors.
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
// Create Context
Context context = new Context(Scheme.Https, "localhost", 8089, default(TimeSpan), handler);
// Create Service
service = new Service(context);

You get this error because your app isn't able to validate the certificate of the connection, and it's especially common to use this for the API that creates the session/login tokens. You can bypass it in a dangerous way as shown above, but obviously that's not a good solution unless you're just testing.
The best and easiest solution is to use the "modernhttpclient-updated" Nuget package, whose code is shared in this GitHub repo where there's also a lot of documentation.
As soon as you add the Nuget package, pass in a NativeMessageHandler into you HttpClient() as shown and build:
var httpClient = new HttpClient(new NativeMessageHandler());
Now you will notice that you got rid of that error and will get a different error message like this Certificate pinning failure: chain error. ---> Javax.Net.Ssl.SSLPeerUnverifiedException: Hostname abcdef.ghij.kl.mn not verified: certificate: sha256/9+L...C4Dw=
To get rid of this new error message, you have to do add the hostname and certificate key from the error to a Pin and add that to the TLSConfig of your NativeMessageHandler as shown:
var pin = new Pin();
pin.Hostname = "abcdef.ghij.kl.mn";
pin.PublicKeys = new string[] { "sha256/9+L...C4Dw=" };
var config = new TLSConfig();
config.Pins = new List<Pin>();
config.Pins.Add(pin);
httpClient = new HttpClient(new NativeMessageHandler(true, config)
Keep in mind that your other (non token generating) API calls may not implement certificate pinning so they may not need this, and frequently they may use a different Hostname. In that case you will need to register them as pins too, or just use a different HttpClient for them!

Installing the .NET Core SDK installs the ASP.NET Core HTTPS development certificate to the local user certificate store. The certificate has been installed, but it's not trusted. To trust the certificate, perform the one-time step to run the dotnet dev-certs tool:
dotnet dev-certs https --trust
for more information visit this link

I had to turn off my vpn to get rid off this error

Related

In the VaultSharp library, what's the equivalent of setting the VAULT_CACERT environment variable?

I'm getting the error An error occurred while sending the request when using the VaultSharp library in C# to request secrets from a Vault service. I can get the access token I need from the command line, so I know the Vault address and my personal Vault token work.
The CLI relies on the environment variables VAULT_ADDR, VAULT_TOKEN and VAULT_CACERT. I see VaultSharp creates the VaultClientSettings object using the first two: address and token information--but where in VaultSharp can I specify the CA certificate path?
Here's the code I'm using, copied from https://github.com/rajanadar/VaultSharp/blob/master/README.md:
string vaultToken = Environment.GetEnvironmentVariable("VAULT_TOKEN");
VaultSharp.V1.AuthMethods.IAuthMethodInfo authMethod = new VaultSharp.V1.AuthMethods.Token.TokenAuthMethodInfo(vaultToken);
string vaultAddress = Environment.GetEnvironmentVariable("VAULT_ADDR");
var vaultClientSettings = new VaultSharp.VaultClientSettings(vaultAddress, authMethod);
VaultSharp.VaultClient vaultClient = new VaultSharp.VaultClient(vaultClientSettings);
string vaultRoute = Properties.Settings.Default.VaultRoute;
VaultSharp.V1.Commons.Secret<VaultSharp.V1.Commons.SecretData> kv2Secret = await vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(vaultRoute);
It's this last statement ReadSecretAsync that throws the error.
Many thanks for your help!
There is no equivalent of VAULT_CACERT in VaultSharp. VaultSharp expects your Vault URL to have a trusted SSL Cert. If not, you will get TLS errors while establishing the handshake. And in non-prod environments, folks typically use the following snippet to solve for it.
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true; // or do specific checks

Could not create SSL/TLS secure channel while calling pages within the site

I am working on a web call that is supposed to do some checking on WSDL pages within the same site. I thought this would be simple. It works fine until we add in the certificate call and then the SSL breaks
var httpClientHandler = new HttpClientHandler
{
SslProtocols = SslProtocols.Tls12,
ServerCertificateCustomValidationCallback = (message, certificate2, chain, errors) => { return true; }
};
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "fakename.com", false);
httpClientHandler.ClientCertificates.AddRange(collection);
HttpClient client = new HttpClient(httpClientHandler);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpResponseMessage resp = client.GetAsync(uri, HttpCompletionOption.ResponseContentRead, token).Result;
the user clicks the healthcheck link
I construct a full uri for the WSDL.
the code above runs and I read the respnse.
Unit testing shows this working fine, the difference is that I am making the call outside of the site, from that I can deduce that the certificate does get added, and a call is possible.
Has anyone else seen this happen? Is there some rule I need to observe in order to call a page from within my own site behind SSL?
I am answering my own question...
when I was running the unit tests the call was coming from outside the site and simply used the public .cer file for the client certificate.
when creating the same call from within the site I had to grab the private key file and apply the password.
var clientCertificate = new X509Certificate2(_509FilePath, "my password");
I think I would have expected a 403 error rather than the SSL issue, but hey... at least it's working now... thanks #CodeCaster for the hint in the comments

.NET Core - HTTPClient - dh key too small on Ubuntu 20.04

I'd like to ask if there's a way to bypass Ubuntu's security checks so that I could be able to fetch a website with a small key in my .NET Core Client app? I am getting error:141A318A:SSL routines:tls_process_ske_dhe:dh key too small exception.
The issue is that in Ubuntu 20.04 openSSL has security level set to 2 and (currently, hopefully someone will come up with an answer for my question on Ask Ubuntu) I have no idea how to set it to a lower value.
The same error occurs using curl unless --ciphers 'DEFAULT:!DH' parameter is provided, so I assume the root cause of the problem is within the operating sysem itself.
I do not control the website's server, so changing its security settings is a no go.
What I've tried so far from C# side:
serviceCollection.AddHttpClient<IInterface, IImplementation>()
.ConfigureHttpMessageHandlerBuilder(messageHandlerBuilder =>
{
messageHandlerBuilder.PrimaryHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (m, c, ch, e) => true
};
});
and
using var httpClient = new HttpClient();
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
var websiteContent = await httpClient.GetStreamAsync(url);
Security is not much of an issue in this case so I'm ok with using any dirty hack here.
Any help would be much appreciated.
Thanks to the answer received on Ask Ubuntu I managed to fix the issue by:
copying openssl.cnf file
Adding openssl_conf = default_conf at the top of the copied file
Adding at the end:
[ default_conf ]
ssl_conf = ssl_sect
[ssl_sect]
system_default = ssl_default_sect
[ssl_default_sect]
MinProtocol = TLSv1.2
CipherString = DEFAULT:#SECLEVEL=1
Running the project with OPENSSL_CONF environmental variable set to path to the altered config file

How it works the security in gRPC?

I have a service that I am configurating in this way:
options.Listen(miAddress, 5001, l =>
{
l.Protocols = HttpProtocols.Http2;
System.Security.Cryptography.X509Certificates.X509Certificate2 miCertificado = new System.Security.Cryptography.X509Certificates.X509Certificate2(#"certificados\service.crt");
l.UseHttps(miCertificado);
});
But if I realized that I can configure the client to avoid the authentication, with this code:
var httpClientHandler = new System.Net.Http.HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback =
System.Net.Http.HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new System.Net.Http.HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress(_serviceAddress,
new GrpcChannelOptions { HttpClient = httpClient });
var client = new Gestor.GestorClient(channel);
In this case, the authentications is ignored and I can use call to the service.
I know that this ignore the authentication because if I try to use the client to use a certificate, I get an error that tells that the connection couldn't be stablish because of the SSL.
So my doubt is, there is some way to set the service to don't allow this kind of connections? If not, anyone could create a client that igonres this authentication and the security has no sense.
Thanks.
Using HTTPS is not the same as using authentication. All you're doing is encrypting the traffic between client and server, so that eavesdroppers can't read your plaintext traffic.
If you configure your client to accept any server certificate, whether that certificate is valid up till its root or not, does not "ignore authentication" - there was no authentication to begin with.

Ignore bad certificate - .NET CORE

I'm writing a .NET Core app to poll a remote server and transfer data as it appears. This is working perfectly in PHP because the PHP is ignoring the certificate (which is also a problem in browsers) but we want to move this to C# .NET CORE because this is the only remaining PHP in the system.
We know the server is good, but for various reasons the certificate can't / won't be updated any time soon.
The request is using HttpClient:
HttpClient httpClient = new HttpClient();
try
{
string url = "https://URLGoesHere.php";
MyData md = new MyData(); // this is some data we need to pass as a json
string postBody = JsonConvert.SerializeObject(md);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage wcfResponse = await httpClient.PostAsync(url, new StringContent(postBody, Encoding.UTF8, "application/json"));
Console.WriteLine(wcfResponse.Content);
}
catch (HttpRequestException hre)
{
// This exception is being triggered
}
Having researched this it seems the universal recommendation is to use ServicePointManager, but this is not available in .NET Core and I'm having trouble finding the recommended replacement.
Is there a simple or better way to do this in .NET Core?
Instead of new HttpClient() you want something akin to
var handler = new System.Net.Http.HttpClientHandler();
using (var httpClient = new System.Net.Http.HttpClient(handler))
{
handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
// Log it, then use the same answer it would have had if we didn't make a callback.
Console.WriteLine(cert);
return errors == SslPolicyErrors.None;
};
...
}
That should work on Windows, and on Linux where libcurl is compiled to use openssl. With other curl backends Linux will throw an exception.
Getting Linux and macOS to work
If you are working in Linux or macOS you may encounter a scenario in which HttpClient will not allow you to access a self signed cert even if it is in your trusted store. You will likely get the following:
System.Net.Http.CurlException: Peer certificate cannot be authenticated with given CA certificates environment variable
of if you are implementing (as shown in the other answer)
handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
// Log it, then use the same answer it would have had if we didn't make a callback.
Console.WriteLine(cert);
return errors == SslPolicyErrors.None;
};
This is due to the version of libcurl on the machine not supporting the appropriate callbacks that .Net Core needs to in order to gather the appropriate data to call the ServerCertificateCustomValidationCallback. For example, there isn't a way for the framework to create the cert object or another one of the parameters. More information can be found in the discussion for the workaround that was provided in .NET Core at issue in dotnet core's github repo:
https://github.com/dotnet/corefx/issues/19709
The workaround (which should only be used for testing or specific internal applications) is the following:
using System;
using System.Net.Http;
using System.Runtime.InteropServices;
namespace netcurl
{
class Program
{
static void Main(string[] args)
{
var url = "https://localhost:5001/.well-known/openid-configuration";
var handler = new HttpClientHandler();
using (var httpClient = new HttpClient(handler))
{
// Only do this for testing and potentially on linux/mac machines
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && IsTestUrl(url))
{
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
var output = httpClient.GetStringAsync(url).Result;
Console.WriteLine(output);
}
}
static bool IsTestUrl(string url) => url.Contains("localhost");
}
}
There is another avenue for fixing this problem, and that is using a version of libcurl that is compiled with openssl support. For macOS, here is a good tutorial on how to do that:
https://spin.atomicobject.com/2017/09/28/net-core-osx-libcurl-openssl/
For the short version, grab a copy of the latest libcurl compiled with openssl support:
brew install curl --with-openssl
You probably don't want to force the entire OS to use the non-Apple version of libcurl, so you'll likely want to use the DYLD_LIBRARY_PATH environment variable instead of using brew to force link the binaries into the regular path of the OS.
export DYLD_LIBRARY_PATH=/usr/local/opt/curl/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}
The above command can be used to set the appropriate environment variable when running dotnet in a terminal. This doesn't really apply to GUI applications though. If you're using Visual Studio for Mac, you can set the environment variable in the project run settings:
The second approach was necessary for me when using IdentityServer4 and token authorization. The .NET Core 2.0 authorization pipeline was making a call to the token authority using an HttpClient instance. Since I didn't have access to the HttpClient or its HttpClientHandler object, I needed to force the HttpClient instance to use the appropriate version of libcurl that would look into my KeyChain system roots for my trusted certificate. Otherwise, I would get the System.Net.Http.CurlException: Peer certificate cannot be authenticated with given CA certificates environment variable when trying to secure a webapi endpoint using the Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)] attribute.
I spent hours researching this before finding work arounds. My whole goal was to use a self-signed certificate during development for macOs using IdentityServer4 to secure my webapi. Hope this helps.
//at startup configure services add the following code
services.AddHttpClient(settings.HttpClientName, client => {
// code to configure headers etc..
}).ConfigurePrimaryHttpMessageHandler(() => {
var handler = new HttpClientHandler();
if (hostingEnvironment.IsDevelopment())
{
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
}
return handler;
});
now you can use IHttpClientFactory CreateClient method within your service
Just to add another variation, you could add in your thumbprint and check it in the callback to make things a bit more secure such as:
if (!string.IsNullOrEmpty(adminConfiguration.DevIdentityServerCertThumbprint))
{
options.BackchannelHttpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => certificate.Thumbprint.Equals(adminConfiguration.DevIdentityServerCertThumbprint, StringComparison.InvariantCultureIgnoreCase)
};
}
adminConfiguration.DevIdentityServerCertThumbprint is the configuration that you would set with your self signed cert's thumbrint.

Categories

Resources