_From @ChrML on Thursday, September 5, 2019 7:09:29 AM_
From ASP Core 3.0 preview6 and newer it is now possible to get a grade B, instead of a previous grade F, on the SSL test (https://www.ssllabs.com/ssltest/). Because from 3.0 the insecure cipher suits are now disabled by default.
But still in 3.0-preview8, the Kestrel server does not have any cipher suit preference. This caps the SSL test grade to B instead of A. Grade A servers are regarded the most secure servers.
The server should prefer the "Perfect Forward Secrecy" compliant cipher suits (ECDHE) over the weaker cipher suits.
Here is the SSL report from the cipher suit chapter (on a 3.0-preview8 application):
Cipher Suites
TLS 1.2 (server has no preference)
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f) WEAK 128
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013) ECDH secp521r1 (eq. 15360 bits RSA) FS WEAK 128
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c) WEAK 128
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c) WEAK 128
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027) ECDH secp521r1 (eq. 15360 bits RSA) FS WEAK 128
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) ECDH secp521r1 (eq. 15360 bits RSA) FS 128
TLS_RSA_WITH_AES_256_CBC_SHA (0x35) WEAK 256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014) ECDH secp521r1 (eq. 15360 bits RSA) FS WEAK 256
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d) WEAK 256
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d) WEAK 256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028) ECDH secp521r1 (eq. 15360 bits RSA) FS WEAK 256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) ECDH secp521r1 (eq. 15360 bits RSA) FS 256
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8) ECDH secp521r1 (eq. 15360 bits RSA) FS 256
Here is the description from SSL labs why the Kestrel server is capped to grade B:
Penalty for not using forward secrecy (B)
Forward secrecy (FS) also known as perfect forward secrecy (PFS), is a property of secure communication protocols in which compromises of long-term keys does not compromise past session keys. Forward secrecy protects past sessions against future compromises of private key. The very popular RSA key exchange doesn鈥檛 provide forward secrecy. You need to support and prefer ECDHE suites in order to enable forward secrecy with modern web browsers.
SSL Labs will start penalizing servers that don鈥檛 support forward secrecy; Grade will be capped to B. We will not penalize sites that use suites without forward secrecy provided they are never negotiated with clients that can do better.
Simply making the server prefer the existing ECDHE cipher suits over the weak cipher suits will suffice for getting a grade A.
_Copied from original issue: aspnet/AspNetCore#13700_
_From @Tratcher on Thursday, September 5, 2019 10:16:26 AM_
Was this Windows, Linux, or Mac? You'll get different results because Kestrel does not control this ordering, it's controlled by the operating system (or OpenSSL for Linux).
_From @ChrML on Thursday, September 5, 2019 10:36:44 AM_
Was this Windows, Linux, or Mac? You'll get different results because Kestrel does not control this ordering, it's controlled by the operating system (or OpenSSL for Linux).
Hosted by Linux under this official Docker- image: https://hub.docker.com/_/microsoft-dotnet-core-aspnet (mcr.microsoft.com/dotnet/core/aspnet:3.0)
We have not really touched the OS at all, just copied the application to inside the official Docker image and deployed it to a Kubernetes cluster.
_From @Tratcher on Thursday, September 5, 2019 12:15:13 PM_
I'll forward this to the SslStream owners to see if it's configurable in their OpenSsl usage.
Thanks. Following.
cc: @krwq
@bartonjs
AFAIR OpenSSL and SecureTransport should respect sorting when using CipherSuitePolicy. The TLS implementations might re-sort the list and put TLS1.3 cipher suites first though (which should still grade it as A).
If you prefer to use default settings with "prefer forward secrecy" I'd need to investigate if there is a direct option for that anywhere (can't find by quick looking) and if not we can likely emulate it using CipherSuitePolicy (i.e. read the OS defaults and provide it as a list) but we'd need API proposal for that.
One thing to try is to upgrade your OpenSSL to latest and see if the settings improved.
EDIT:
If you meant that server order is correct but server will pick with client preference then there is a setting SSL_OP_CIPHER_SERVER_PREFERENCE for that (per https://www.openssl.org/docs/man1.1.1/man3/SSL_set_options.html) - I have mixed feelings if this should be default and rather add new API for controlling that
The server has no preference at all regarding cipher suit ordering, it only lists which cipher suits it supports (with PFS suits being in the list, but not preferred by the server).
Do you mean that adding a CipherSuitePolicy to the code with the wanted cipher suits as a manually sorted list, should make the server prefer them in that order? I was under the impression that CipherSuitePolicy only changed the supported suites, but didn't enable server preference. I need to test this. If that is the case, it should probably be added to the documentation.
I think that the server by default should prefer the most secure cipher suits though. We use the ASP Core 3.0 docker image, not sure which version of OpenSSL is included in that image.
@ChrML, yes specifying CipherSuitePolicy manually will do best attempt to respect the given order (both on server and client) but order it is not guaranteed since it's underlying implementation which eventually decides.
SSL_OP_CIPHER_SERVER_PREFERENCE setting will not be changed though with that so I believe it's client order which wins by default (at least with OpenSSL, unsure with SecureTransport) - the only exception to that is TLS 1.3 - if both sides negotiate any TLS 1.3 cipher suites the TLS 1.3 cipher suite will be preferred.
I think that the server by default should prefer the most secure cipher suits though.
Well, this is rather arbitrary - some will say ChaCha20 is better than AES some claim otherwise.
Also note that cipher suites are sent plaintext in the client hello which implies that MITM is able to degrade chosen cipher suites to the weakest possible and if degradation leads to breaking bad crypto then Finished message which contains MAC of the previous messages (including client hello) can also be forged so choosing "best" one will not give you much. You should trust all of cipher suites you set on the server and client and if you're not comfortable with any of them being chosen you should remove it from the list.
The easiest way to achieve "safe" settings is to use only TLS 1.3 which allows only 5 "safe" cipher suites
@krwq Yes, client order will win by default. This does not matter though, because the SSL Cert rating for the server will only consider what the server prefers. There is a seperate SSL Cert test for clients which considers client ordering, but that is outside the scope of what a server application developer can control.
What is important is that the server has a cipher suit preference. And that the servers cipher suits preferred by the server support PFS and is not a "broken" cipher suit (eg proven insecure). This is all that is required to get a A grade rating for the server. Whether this cipher suit will actually be negotiated does not matter.
Downgrade attacks is the reason why the server is not allowed to support insecure cipher suits. If the server supports insecure cipher suits (cracked), the SSL tests will automatically degrade the server to grade F. Because the client can be "downgrade-attacked" to this insecure suite by a man in the middle. This can be perfectly handled by the server removing these cipher suits from CipherSuitePolicy as soon as they get cracked.
Default cipher suits in the ASP Core 3.0 docker image seems up to date and has good enough cipher suits to get a grade B. All that is required to get grade A is that the server prefers a cipher suit that is considered "perfect forward secret". At the moment the server has no preference which is the reason it only gets grade B.
(yes, you can get "downgrade-attacked" to a weaker cipher suit that is not PFS, independent of server preference, but that is accepted by SSL test and will not cap the server's grade).
@krwq @Tratcher Is there something actionable here for .NET Core to make this scenario work? Or does this scenario require OS configuration (i.e. env variables) etc.? Trying to decide what to do with this issue.
I think the only actionable thing at this moment might be adding something equivalent to CipherSuitesPolicy.PreferServerOrder but there would have to be some investigation done prior to that as there are some rough edges (i.e. how to express use OS default cipher suites list
since this is currently done by passing (CipherSuitesPolicy)null
and check if this is possible to implement on at least OSX). We might want to consider adding this only to SslServerAuthenticationOptions as well.
In case other people are having the same issue and need a workaround, here is one temporary solution for those using Linux:
Adding this piece of code to Program.cs
configures the Kestrel server for TLS 1.2 and TLS 1.3 only with secure cipher suits. CipherSuitesPolicy only works on non-Windows operating systems.
```c#
.ConfigureKestrel((context, options) =>
{
options.ConfigureHttpsDefaults(httpsOptions =>
{
// Important: Only enable TLS 1.2 and TLS 1.3 to comply with SSL Server tests.
// TLS 1.1, TLS 1.0 and SSLv3 are considered insecure by todays standards.
httpsOptions.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13;
// Configure HTTPS certificate.
if (!String.IsNullOrEmpty(httpsCertificateFile))
{
httpsOptions.ServerCertificate = new X509Certificate2(httpsCertificateFile);
}
// Configure the cipher suits preferred and supported by the server. (Windows- servers are not so keen on doing this ...)
if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
httpsOptions.OnAuthenticate = (conContext, sslAuthOptions) =>
{
sslAuthOptions.CipherSuitesPolicy = new System.Net.Security.CipherSuitesPolicy(
new System.Net.Security.TlsCipherSuite[]
{
// Cipher suits as recommended by: https://wiki.mozilla.org/Security/Server_Side_TLS
// Listed in preferred order.
// Highly secure TLS 1.3 cipher suits:
System.Net.Security.TlsCipherSuite.TLS_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_AES_256_GCM_SHA384,
System.Net.Security.TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256,
// Medium secure compatibility TLS 1.2 cipher suits:
System.Net.Security.TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
System.Net.Security.TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
}
);
};
}
});
});
```
This code yields the following SSL Labs results:
NOTE: For some reason the legacy cipher-suits for TLS 1.2 does not get applied on Linux under the Docker-image mcr.microsoft.com/dotnet/core/aspnet:3.0
.
This may cause older clients to be unable to connect. Such as Windows 7 / 8.1 machines with IE11 and Safari 8 and older browsers will stop working. IE/Safari on newer OS and Chrome/Firefox regardless of OS is still fine.
@ChrML to avoid allocations might be worth storing that CipherSuitesPolicy instance somewhere - otherwise it will be constructed on every request
@krwq Good point, thanks! Did not realize it's called for every request.
@krwq what do you think needs to happen here as next step? Can you please help us clarify?
@karelz this request is to add option to pick between server or client cipher suite preference. So possibly something like:
c#
public partial class SslServerAuthenticationOptions
{
public bool PreferServerOrder { get; set; } // i.e. on OpenSSL that would set SSL_OP_CIPHER_SERVER_PREFERENCE
}
Having said that I do not believe this feature will add any security to any system (it will always be as good as your weakest cipher suite) but it does currently give you better rank on some websites (like mentioned above https://www.ssllabs.com/ssltest/). You can also get good rating as per settings mentioned above
@Tratcher how much do you want this for Kestrel?
cc @samsp-msft
@bartonjs, @anurse can you share your notes from the crypto-board meeting about this?
Effectively, we're going to pursue providing a .NET-specific openssl.cnf, and seed it with some opinions.
FYI:
(_Using Server Side Blazor with Kestrel .NET Core 3.1 LTS on Linux Ubuntu 18.04 VPS with OpenSSL 1.1.1, Let's Encrypt Certificate_)
I've implemented the sample (above) from @ChrML (which is on Kestrel web server implementation in ASP.NET Core as well) and I am using the test suite from the Dutch Government (Websites/Email): https://en.internet.nl/
The score is 97% and the one error I get is:
"Cipher order
Verdict: Your web server does not enforce its own cipher preference ('I').
First found affected cipher pair: none
Test explanation:
We check if your web server, while negotiating with web browsers, suggests ciphers (algorithm selections):
I. Based on its own preference and not that of the web browser. (requirement level: Required);"
And about the sample (answer from @krwq):
to avoid allocations might be worth storing that CipherSuitesPolicy instance somewhere - otherwise it will be constructed on every request
Is it now 'safe' the use the sample from above, as it is on Kestrel web server implementation in ASP.NET Core
@JeepNL
Here is an updated sample that I use in my high-traffic production webserver now. Implemented according to @krwq's recommendations avoiding unneccessary allocations on each request:
From the Program.CreateWebHostBuilder method:
.ConfigureKestrel((context, options) =>
{
options.ConfigureHttpsDefaults(httpsOptions =>
{
if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
httpsOptions.OnAuthenticate = (conContext, sslAuthOptions) =>
{
sslAuthOptions.CipherSuitesPolicy = cipherSuitesPolicy;
};
}
});
});
A field in the Program class:
// Ciphersuit policy for this server:
static readonly System.Net.Security.CipherSuitesPolicy cipherSuitesPolicy = new System.Net.Security.CipherSuitesPolicy
(
new System.Net.Security.TlsCipherSuite[]
{
// Cipher suits as recommended by: https://wiki.mozilla.org/Security/Server_Side_TLS
// Listed in preferred order.
// Highly secure TLS 1.3 cipher suits:
System.Net.Security.TlsCipherSuite.TLS_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_AES_256_GCM_SHA384,
System.Net.Security.TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256,
// Medium secure compatibility TLS 1.2 cipher suits:
System.Net.Security.TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
System.Net.Security.TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
System.Net.Security.TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
System.Net.Security.TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
}
);
@ChrML Thank you for the example. With your code I get an "A+" rating at SSL Labs.
FYI: I've added some extra ciphers
// From: https://en.internet.nl
// High
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
// Medium
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_AES_128_GCM_SHA256,
TlsCipherSuite.TLS_AES_256_GCM_SHA384,
TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256
I've also this in Startup.cs
_ConfigureServices:_
```c#
services.AddHsts(opts =>
{
opts.Preload = true;
opts.IncludeSubDomains = true;
opts.MaxAge = TimeSpan.FromDays(190);
});
_Configure:_
```c#
app.UseHsts();
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Frame-Options", "DENY");
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("X-XSS-Protection", "1; mode = block");
context.Response.Headers.Add("Referrer-Policy", "strict-origin");
await next();
});
https://en.internet.nl is a internet security test suite from the Dutch Internet Community and the Dutch Government (see: about). It follows the guidelines from NCSC.nl (_National Cyber Security Centrum_) They recently updated the guidelines to comply with the newest internet standards.
With Kestrel the HTTPS test fails because Kestrel does not enforce the 'Cipher Order', it lets the browser do this (_see images_)
I hope the team can take a look at this for .NET Core 5.
@JeepNL Yes, it seems SSL Labs does not reduce the score because of lack of serverside cipher-suite preference. It does however notice and comments it, but still passes as A+.
Other stricter SSL tests are per Kestrel today impossible to pass without a reverse proxy or load balancer before, or modifying the OS OpenSSL configuration (which is often not feasible because Docker).
@ChrML I'm running it on a plain Ubuntu 18.04 VPS, no Docker. And I really want to run Kestrel stand alone, not with a reverse proxy. Do you maybe have a link to some info on how to configure OpenSSL with Kestrel so it enforces the Cipher Order on the server? I've Postfix installed, and don't get the 'Cipher Order'-error on the email test at https://en.internet.nl - It scores a 100%. It's installed on de same server.
Oops ... It's probably this link from @krwq https://www.openssl.org/docs/man1.1.1/man3/SSL_set_options.html / SSL_OP_CIPHER_SERVER_PREFERENCE
@JeepNL Sorry, never done it myself, don't know how. Maybe someone else here does. When I was googling this issue, there were other people suggesting to do that.
@chrml Found something earlier in this thread, updated my previous comment.
Theoretically, setting server preference is possible via openssl.cnf, though I've not personally tried it. I think the full waterfall is
openssl_conf = default_conf
[default_conf]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
Options = ServerPreference
(where the top line in my snippet here has to occur before the first [ header ]
in the file, IIRC).
@bartonjs IT WORKS! 100%! I just did a copy/paste of your example and put it before the first [ header ] of "/etc/ssl/openssl.cnf". (_Server Side Blazor with Kestrel .NET Core 3.1 LTS, Ubuntu 18.04 VPS. OpenSSL 1.1.1_) - Restarted my Kestrel service and tested it at https://en.internet.nl.
I was a bit worried first because I install the latest .NET Core via SNAP and I found a couple of more locations of openssl.cnf in SNAP dirs but I just needed to edit the default (OS) one. It was really that simple. 1st try and it just worked.
Thank you all so much for your help/info.
is there something we need to do for 5.0 @bartonjs or should we just close it for now and document it via issue @davidfowl linked?
It's something I'm actively working on for 5.
is there separate issue or should I assign this to you and perhaps change area? If you proceed with the plan we talk about it while back, there won't by any changes to SslStream, right?
oops. missed the self assignment.
there won't by any changes to SslStream, right?
Currently it looks like the changes will be in the native shim; but since SslStream is the only affected component the area path seems correct.
We need to verify the E2E tests tp make sure this covers everything.
@Tratcher is it something you can run easily?
We also need to document the behavior and how to opt-out.
Running the SslLabs test is hard because it requires public IPs, DNS, certs, etc..
Writing functional or integration tests for Kestrel to ensure it's meeting certain standards is quite easy, we just need to know what to check for. Does someone have a list of the criteria?
Tests that need to tweak a machine wide resource like openssl.cnf are a different issue though.
We should do a manual test once we get all the features integrated.
https://github.com/drwetter/testssl.sh/ is pretty useful in this regard.
I did manual testing on Azure image with Ubuntu18.04.
dotnet new mvc
dotnet run
It would get A without trust issue. So we seems to be good there.
I also did check with https://en.internet.nl mentioned above. It does pass the cipher test failed before and the scanner only complain about lack of HSTS (and trust of course)
Unless somebody objects or want more tests I'm going to close this as finished.
Thanks @wfurt, that is awesome result!
What is the "trust issue"? Is it the fact we do not support self-signed certificates?
Thanks @wfurt, that is awesome result!
What is the "trust issue"? Is it the fact we do not support self-signed certificates?
Agree, this is awesome, and exactly what I was hoping for. A new application should be secure by default.
Trust issue has nothing to do with our application. It's just that the tested application was configured with a self-signed certificate that is not trusted by any root authority. It's expected unless you obtain a signed certificate for the test, or automate this test with a service like Lets Encrypt.
yes, this is because I used self-signed certificate and there is no good how to make it "trusted" by the tools.
As well as I used the default dev cert with "localhost" and I'm scanning "toweinfu.westus2.cloudapp.azure.com" so the name does not match. It should not really matter since the work was focus on protocol versions and ciphers.
Not that this is not applicable to old systems with OpenSSL 1.0. Since they cannot do CipherPolicy and configurations, we excluded them for compat reasons.
fixes with sequence of linked PRs
Using the Docker image mcr.microsoft.com/dotnet/core/aspnet:3.1
and the settings specified in this other issue it doesn't seem to work (the settings are completely ignored).
I'm also using Lettuce Encrypt which might be overriding some configurations (but doesn't seem so looking at the source code).
Even though I explicitly disable TLS 1.1 and TLS 1.0 via:
httpsOptions.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13;
for example, I still get:
@shoe-diamente I don't think these changes have made it to .NET Core 3.1. It's scheduled for .NET 5.
@ChrML I see, thanks.
Most helpful comment
Theoretically, setting server preference is possible via openssl.cnf, though I've not personally tried it. I think the full waterfall is
(where the top line in my snippet here has to occur before the first
[ header ]
in the file, IIRC).