Local Server SChannel Error 36888 - ssl

Running Windows Server 2012, I've been getting these errors ever since we converted to HTTPS a long time ago.
As I understand it, the errors are causes by devices connecting with old ciphers, that are not accepted by our server.
Until recently, the cipher stack was set to the server default but after an SSL Labs audit, we decided to remove some of the more outdated ciphers, which allowed us to attain a grade 'B' status.
Mainly RC4.
SSL Labs recommends taking out even more ciphers in order to attain a secure grade 'A' status.
While no doubt there are more casualties, the bottom line is that there has always been a great number of schannel errors - even before tweaking the cipher stack.
I have read many posts here about how to resolve such errors but it seems there are only two solutions: add more (potentially unsafe) ciphers back into the stack; or accept that safety comes first.
First question: is my understanding in the above statement correct?
Second question: what happens on the client web browser when an error occurs?
And finally, is there a 'recommended' cipher stack for IIS 8/Windows Server 2012? It seems that there are many more ciphers mentioned in posts than are in the actual stack.

Related

During handshake of TLS connection where does the client fetch the list of cipher suites from?

From where does the client fetches the list of cipher suites that it is going to propose to server?
For example, it looks like that on same machine, clients of two different applications can propose the different list of cipher suits.
To limit the scope of question, I want to understand it for RabbitMQ Client and TLS1.2.
More clarification
So I have RabbitMQ service installed on my machine and then in C# code I try to connect (Ex. var connection = factory.CreateConnection()) to it without any cipher suits configuration.
So here my C# application could be considered as Client.
please note that I am asking about the ciphers that client sends in 'Client Hello' message.
P.S. somewhere I read that rabbitmq internally uses openssl, so when I ran the command 'openssl ciphers -s -tls1_2' I got a list that is different when I see rabbitmq client hello's cipher suits in wireshark. Also I don't have any group policy on machine that overrides the ciphers.
See https://tls13.ulfheim.net/ for a nice graphical representation of a TLS 1.3 exchange.
In the second message, ClientHello, the client gives among other things a list of ciphers it supports. Where it gets it? Either hard-coded in the application or computed dynamically once decided to connect, etc. this all depends on the application, so you need to investigate its code source/its configuration.
If you take for example openssl s_client as TLS client, you have -cipher and -ciphersuites configuration options to specify which specific ciphers to announce when connecting.
If you look later in the exchange, there is a ServerHello where the server announces which cipher suite will be used.
How that happens? Typically, the server has its own list of preferred ciphers ordered by "quality" (cryptographic strength like preferring 256bits over 128bits and/or other properties like preferring PFS over non PFS), and based on what it got from the client, it tries to select the "best" one that is supported on both side, which is not necessarily always possible.
Now as you put in comments a specific application (Google Chrome), you can see at https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/ssl_cipher.cc;l=1152?q=cipher&ss=chromium%2Fchromium%2Fsrc the definition of a function called "ssl_create_cipher_list" whose goal is to build the cipher list, starting from all compiled ones, and then applying various rules to enable/disable some and sorting them in an order that makes sense. This is of course highly dependent on the application for how it is done, but you can certainly find similar operations in other toolkits.
Finally, do note that there is a big difference between TLS 1.2 (ciphers) and TLS 1.3 (ciphersuites). They are disjoint sets, and when TLS 1.2 has "myriad" of ciphers in the wild (see for example https://www.openssl.org/docs/man1.1.1/man1/ciphers.html for lots of them), TLS 1.3 defines only 5 ciphersuites (see https://datatracker.ietf.org/doc/html/rfc8446#appendix-B.4) with one being mandatory to implement, so the whole question of auto-negotiation between client and server becomes almost moot.
As you mention explicitly tls1.2 as tag, do note there is no real reason today not to use 1.3 instead (which is why my first link is on purpose specially for 1.3 - the handshake routine is different in 1.2 but identical for things related to your question of ciphers), you will gain a lot of benefits, from simplified operations, better cryptography all around, and fewer holes.

Methods for updating SHA-1 fingerprint on IOT devices

Disclaimer: Complete newbie trying to wrap my head around SSL.
I am developing a device using an ESP8266 which needs to connect securely to a known server for IOT purposes, we will develop and control the server endpoint as well as the ESP8266 based client (BearSSL etc), we will not control the SSL certificate updates on the hosted server and need to manage the changing certificate keys.
Using the SHA-1 fingerprint for the certificate installed on the server appears to be the most straightforward approach and will provide the basic security we need. The data we will be exchanging is not sensitive or mission critical, we just need to keep the web server happy going into the future.
I understand the need to update the SHA-1 fingerprint on the client when the server certificate updates and this would typically be done with a firmware update over a secure connection. Our use case will make this very difficult for various reasons, so I am trying to establish the best method for updating the fingerprint as it changes without requiring re-flashing/OTA updates.
What I don't understand is why there is a need to protect/hide/embed the fingerprint when any public user or hacker can visit our SSL server site and obtain the fingerprint through a browser or OpenSSL query. Can I simply not retrieve the current fingerprint (maybe encode it with our own basic encryption) from a known HTTP non SSL server perhaps running PHP which will obtain and calculate the current fingerprint of our SSL server for use by our IOT device ? Our device would query the HTTP server first, retrieve the fingerprint and store it in EEPROM until it expires, then simply re-obtain the new fingerprint as required. Then it goes off and talks to the SSL server.
So the crux of the question is, if a hacker can get the fingerprint straight from our SSL server, why would this be an unsafe approach, which I'm sure it is ?
I don't want to go down the trusted root CA with long expiry approach as our devices may need to run for 20-30 years and we'll need a device certificate update procedure regardless, and would prefer not to use ClientInsecure() if possible.
Assuming the non-SSL HTTP approach is no good, can anybody suggest an alternate automated method for retrieving the current fingerprint securely ? I have to assume our devices may get left in a cupboard or disconnected from Wifi for years at a time and need to automatically re-connect in the future without a firmware update.
Many thanks, and be gentle *8)
Your question may be removed as inappropriate for Stack Overflow but it's a really interesting one and I'm hoping you'll at least get a chance to see this answer.
First of all, there is absolutely no need to hide the fingerprint of the server's certificate. As you pointed out, anyone can get the fingerprint directly from the server itself.
If you're downloading the fingerprint from a different source in order to update your embedded device then it's not privacy you need, it's authentication - that you're getting it from the source you think you're getting it from - and integrity - that the fingerprint hasn't been corrupted or modified during transmission.
Which leads you to a chicken and egg problem. If you serve the updated fingerprint through non-HTTPS servers then it's vulnerable to modification and the servers are vulnerable to impersonation. If you serve it via HTTPS then you still have the issue of verifying the HTTPS server you're getting the fingerprint from.
You could use a pre-shared key to sign and verify the downloaded fingerprint. The embedded device would use a public key to decrypt a signed fingerprint, the server would have the private key to sign it. Then you face an entire new set of issues if the private key is every compromised - key revocation and distribution, which is part of the problem you're trying to skirt here with this whole process.
You're also going to want to do better than SHA-1. SHA-1 hasn't been considered cryptographically secure for years.
And in 20 - 30 years time, it's likely that whatever algorithm you're using will also cease to be cryptographically secure. Which means that you'll need to update the fingerprint algorithm over the course of decades.
Instead of using the fingerprint, you can embed in the device's firmware the top level certificate of the Certificate Authority that signed the server's certificate, but CA certificates will also expire well before 20-30 years elapse, and may also be revoked. If you embed the CA certificate then the web server will have to supply the embedded device with its entire certificate chain so that the device can verify signatures at each step, which on an ESP8266 may be very, very slow, even today.
In fact, it's quite likely that web servers 20-30 years from now won't use the same cyphers for SSL as they do today, and it's likely they won't continue to support the version of TLS (1.3) that's standard now. So you would need to be able to update your embedded software to TLS 1.8 or 2.0 or whatever the version will be that's needed 20-30 years from now. And the ESP8266 is not particularly fast at computing today's cyphers... it may be computationally impractical for it to compute the cyphers of decades in the future.
In fact, wifi 20-30 years from now will quite possibly not support hardware from today as well as wifi protocols evolve and also require updated cypher suites.
I'm also dubious that ESP8266's are likely to run continuously for 20 years without hardware failures. The main feature of the ESP8266 is that it's cheap, and cheap does not often correspond with reliability or longevity.
With much better performance, the ESP32 (still cheap) would stand a better chance to being able to compute the cyphers in use 20-30 years from now and support the future's wifi standards, but with its (and the ESP8266's) closed source wifi implementation you'd be at the mercy of Espressif to provide updates to its wifi stack 20 years from now, which I doubt will happen.

gnutls and openssl handshake in NGINX

I'm testing SSL/TLS stream proxying within NGINX that will connect to a web server using gnutls as the underlying TLS API. Using the command line test tool in gnutls (gnutls-serv) the entire process works, but I can't understand the logic:
the NGINX client (proxying HTTP requests from an actual client to the gnutls server) seems to want to handshake the connection multiple times. In fact in most tests it seems to handshake 3 times without error before the server will respond with a test webpage. Using wireshark, or just debugging messages, it looks like the socket on the client side (in the perspective of the gnutls server) is being closed and reopened on different ports. Finally on the successful connection, gnutls uses a resumed sessions, which I imagine is one of the previously mentioned successful handshakes.
I am failing to find any documentation about this sort of behaviour, and am wondering if this is just an 'NGINX thing.'
Though the handshake eventually works with the test programs, it seems kind of wasteful (to have multiple expensive handshakes) and implementing handshake logic in a non-test environment will be tricky without actually understanding what the client is trying to do.
I don't think there are any timeouts or problems happening on the transport, the test environment is a few different VMs on the same subnet connected between 1 switch.
NGINX version is the latest mainline: 1.11.7. I was originally using 1.10.something, and the behaviour was similar though there were more transport errors. Those errors seemed to get cleaned up nicely with upgrading.
Any info or experience from other people is greatly appreciated!
Use either RSA key exchange between NGINX and the backend server or use SSLKEYLOGFILE LD_PRELOAD for NGINX to have the necessary data for Wireshark to decrypt the data.
While a single incoming connection should generate just one outgoing connection, there may be some optimisations in NGINX to fetch common files (favicon.ico, robots.txt).

Can I detect the SSL version that a browser supports?

I would like to display a message to customers who's browser's highest level of encryption is SSLv3. Is it possible for me to target browser settings of SSLv3 and lower? Client or Server code? We will be allowing lower versions of SSL to use our site during a certain grace period. During this grace period, we would like to display a message only to those users that have browser settings of SSL3 or lower.
Not easily. The browser's supported SSL versions are not detectable until the SSL handshake is in progress, and even then only if the browser uses an SSLv2 handshake to allow dynamic version negotiation. If an unsupported version were detected, you would not be able to send a message back since the handshake failed and the connection would be closed before you could send any message. However, SSL itself has an error packet that gets sent during handshaking, and it can specify a version mismatch error.
The best you can do in your own code is support all SSL versions on the server side, let the client complete a handshake normally, and then detect which version was actually used and send back a message if the SSL version is too low.
Or, you could simply enable TLSv1 or higher only, and simply refuse to let older clients connect at all. They just would not get a nice error message unless the browser decided to detect the SSL version mismatch error and display its own pretty message about it.
Firstly, nowadays, you can generally forget about clients that don't support at least SSLv3. SSLv3 has been widely available for many years.
The TLS Client Hello message, sent when the connection is initiated by the browser, should contain the highest TLS version it supports:
client_version
The version of the TLS protocol by which the client wishes to
communicate during this session. This SHOULD be the latest
(highest valued) version supported by the client. For this
version of the specification, the version will be 3.3 (see
Appendix E for details about backward compatibility).
Appendix E is of course worth looking at.
(The Client Hello message will also contain the list of cipher suites the client supports, which is possibly relevant for the general idea of your question.)
Of course, this specification is just a "SHOULD", so a client supporting TLS 1.2 could still send a Client Hello for TLS 1.1, but what would be the point? By doing so it would have no chance ever to use TLS 1.2 anyway. It could be a preference box that is turned off, but that would effectively make it a client that doesn't support the highest version anyway. (If you want anything more subtle, you'd need to build a database of known user agents, which will be partly unreliable, and for which you'd need to analyse the full user agent string to know everything possible about the platform.)
Now, how to convey the content of the Client Hello message to your application is another matter, and depends very much on which SSL/TLS stack you use. It might not even be directly possible without modifying that SSL/TLS library or the server you're using.
This being said, you can generally get the negotiated TLS version during the current session quite easily. Since that version is the "lower of that suggested by the client in the client hello and the highest supported by the server" (i.e. "min(max(client), max(server))"). If your server supports SSLv3, TLS 1.0, TLS 1.1 and TLS 1.2, and since the latest version is TLS 1.2 anyway, what you'll get during your current connection will also be the max currently supported by the client. As long as your server supports the latest version, you should be able to know what the client supports at best from any live connection.
If you're behind Apache HTTP server's mod_ssl, you should be able to get that from the SSL_PROTOCOL environment variable. You should also be able to get the protocol from the SSLSession in Java.
(If you are willing to write a more bespoke service, you could pass further details like the cipher suites more directly to your application, like this service from Qualys SSL Labs does, although I'm not sure if it's meant to be widely available or just a test service.)
I'd have to agree with Remy about it being a bit challenging.
However, a good starting point may be to retrieve some SSL (certificate) information.
Something similar to this:
X509Certificate certChain[] =
(X509Certificate[]) req.getAttribute("javax.net.ssl.peer_certificates");
Another way of getting more information is to retrieve the cipher_suite attribute (similar to the code snippet above).
javax.net.ssl.cipher_suite
I hope this (at least) gets you closer.
Good luck.

Delphi 7 / Indy 9 app not connecting to Apache via SSL

We have an app built in Delphi 7 using Indy 9 for https connections to a php back end running on PHP, on Linux. The app connects to our current production server without a problem. We have been building a test environment for this application in house, and on the test web server, it refuses to connect using https. The app HAS been able to connect to this server using http. I can connect any current browser using the same web calls the app makes (haven't tried older browsers), and I get the connection, and the data returs perfectly, so apparently a there's something in Indy 9 holding this up from working (which is why this question is here and not on ServerFault) Does anyone have enough experience with Indy / SSL to know what's happening here?
Here are the few differences I thought might have some input:
1. I was initially starting with a wildcard cert. It is valid, and on the correct domain. It works with everything else we have on lots of servers.
2. Thinking it was possible that wildcard certs are a newer thing than Indy 9, I removed the wildcard cert, and put in a self signed cert, with the full machine name. It failed in the same way. BUT, as you know, browsers issue a cert exception with these self signed certs, so I was unsure if this would ALSO give Indy problems.
3. I'm sure the version of OpenSSH on our new development server is newer than the one that is currently on our production server. I can dig out these exact versions if someone thinks it will help.
This is an app I inherited, so my knowledge of SSL and Indy is limited. This app is at the end of it's life, so upgrading to Indy 10 is not something my company is going to fund. Any help or pointers or hints are extremely welcome, and my thanks would be infinite.
Thank you so much Stackers!
There's no mention of the specific error that is going on, so to offer suggestions without the particular error message is difficult.
1) Track down the actual responses you are receiving in the SSL handshake and try to make heads or tails of the messages. (DoStatusInfo())
2) (Big guesss here) SSL connectivity comes in different versions where the older versions are out of date and no longer recommended to be supported. If you are building a new server, it likely has defaults that are different than the current production machine which I'm assuming to be a much older build. (The older it is, the more applicable this guess is.) The newer servers should hopefully be refusing to connect to SSL V2 and V3 (and only allowing TLS) So it could be the difference of configuration of old production server and new test server and this new one is filtering out the older SSL versions and preventing the handshake to continue. (Which would be shown in handshake failures in #1 which is your real tool to use to answer this question.)
Here's a quick test of connectivity using openssl.exe. Try this connection to the test box and to the production machine to see if the connection is made. You can try v3 as well to either verify or eliminate this guess...if they act the same, then concentrate on #1. :)
openssl s_client -ssl2 -connect yourhost:443
Try enabling the sslvrfPeer flag in the TIdSSLIOHandlerSocket.VerifyMode property and then return True in the TIdSSLIOHandlerSocket.OnVerifyPeer event.