Unable to decrypt TLS 1.3 packets with the keys got using SSL_CTX_set_keylog_callback openssl API in Wireshark - ssl

I'm working on a task where i need to decrypt all the TLS 1.3 encrypted packets in wireshark (using Edit->Preferences->Protocol->TLS->pre-Master_secret log filename option) for debugging purpose. so i stored all the keys of TLS 1.3 sessions by setting the call back function with openssl provided API() call back function into a file as shown below:
SSL_CTX_set_keylog_callback(pCtx_m,Keylog_cb_func);
void Keylog_cb_func(const SSL *ssl, const char *line) {
// Code to log the line into a file in append mode
}
With this, i'm able to decrypt the TLS 1.3 packets with the keys logged into the file for only the below three ciphers in wireshark
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256
and the other two ciphers of TLS1.3 TLS_AES_128_CCM_8_SHA256 , TLS_AES_128_CCM_SHA256 is not getting decrypted using the keys (in wireshark) opensll API has given though the calls are established over these two ciphers successfully.
I have read this post in stackoverflow about the default enabled cipher suite but i'm not quite sure about if this issue is something related to that. Could anyone help me to understand what i'm doing wrong here:
Sample Key File:
SERVER_HANDSHAKE_TRAFFIC_SECRET dd228de6d3f32ae5d83a9009c2e5908cb71c16d8624f5930dd05cabea3b7cc63 aa48ffe195090f87138caf32a520ecd41644f23f6d778f6436b5e2d697452572
CLIENT_HANDSHAKE_TRAFFIC_SECRET dd228de6d3f32ae5d83a9009c2e5908cb71c16d8624f5930dd05cabea3b7cc63 415e363721b8e204d3e2a2f94682d25792f565770a0f9221e86738d1c540e21b
EXPORTER_SECRET dd228de6d3f32ae5d83a9009c2e5908cb71c16d8624f5930dd05cabea3b7cc63 25276ba5f066f69b6caac00d35e981ff0b7d70e20d541c9435538d32a640ecdc
SERVER_TRAFFIC_SECRET_0 dd228de6d3f32ae5d83a9009c2e5908cb71c16d8624f5930dd05cabea3b7cc63 9d683bacbb0ce07fa5d0425c2763920f11d98af85c7461a52e26efb6bdaf74b5
CLIENT_TRAFFIC_SECRET_0 dd228de6d3f32ae5d83a9009c2e5908cb71c16d8624f5930dd05cabea3b7cc63 bfca7bc330de3836b0e49619375b504f4d2794206c4ecc7fcd1dfdab363bd36a
SERVER_HANDSHAKE_TRAFFIC_SECRET cb2136f271de0e168eb92a9f2a6811c76fe87d08c9b4ae374f4efc72ccdc7ea5 1be3cc095c1e336168ab10619c266eaf7a9057edf724d65f3016ba64d642ab0f
EXPORTER_SECRET cb2136f271de0e168eb92a9f2a6811c76fe87d08c9b4ae374f4efc72ccdc7ea5 0cfa53e94a8a6cf085af19a45a062152299b5ccea84cbffe237f136f0854672e
SERVER_TRAFFIC_SECRET_0 cb2136f271de0e168eb92a9f2a6811c76fe87d08c9b4ae374f4efc72ccdc7ea5 7de206865a34e1b3b004d47075d534ebfd60b47a16ce525f3c0da47c9c78c57f
CLIENT_HANDSHAKE_TRAFFIC_SECRET cb2136f271de0e168eb92a9f2a6811c76fe87d08c9b4ae374f4efc72ccdc7ea5 1a4cad91d84607f44cd6a65cf1e9bcddaf3488d2dcb67b5246995485226dfa5c
CLIENT_TRAFFIC_SECRET_0 cb2136f271de0e168eb92a9f2a6811c76fe87d08c9b4ae374f4efc72ccdc7ea5 be0df5b77fb5edb1785118e6adb119a5820beacb74d01d8cdee344d3feb56488
Wireshark Version: Version 3.6.1 (v3.6.1-0-ga0a473c7c1ba).
Thanks in advance
Prakash

It's found that wireshark Version 3.6.7 (v3.6.7-0-g4a304d7ec222) itself currently has an issue in the TLS 1.3 packet decryption feature for these two ciphers (LS1.3 TLS_AES_128_CCM_8_SHA256 , TLS_AES_128_CCM_SHA256 ) due to invalid-aad-length, and the same would be fixed in the upcoming version. We have reported a bug to the wirshark forum as in the link below
https://gitlab.com/wireshark/wireshark/-/issues/18277

Related

WebSocketpp handshake issue with TLS

I have been learning with WebSocket++ and built some of the server examples (Windows 10 Visual Studio 2019). The non-TLS examples work without issues, however, the TLS-enabled examples (echo_server_both.cpp and echo_server_tls.cpp) can't do the handshake. I am very new to web development in general so I know I must be doing something wrong with regards to the certificate and keys.
I am testing the servers with WebSocket King client, an extension of Google Chrome that connects correctly to other websocket servers like wss://echo.websocket.org and to my own localhost when I don't use TLS.
The echo_server_both example comes with a server.pem file, and the echo_server_tls example comes with server.pem and dh.pem. I have used the same files that come with the samples, and I have also tried generating and registering my own .pem files using openSSL. In both cases I get this when the client tries to connect:
[2021-06-29 20:51:21] [error] handle_transport_init received error: sslv3 alert certificate unknown
[2021-06-29 20:51:21] [fail] WebSocket Connection [::1]:63346 - "" - 0 asio.ssl:336151574 sslv3 alert certificate unknown
[2021-06-29 20:51:21] [info] asio async_shutdown error: asio.ssl:336462231 (shutdown while in init)
I discovered these errors after I edited handle_init() in tls.hpp, following a suggestion in another site, to look like this:
void handle_init(init_handler callback,lib::asio::error_code const & ec) {
if (ec) {
//m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
m_ec = ec;
} else {
m_ec = lib::error_code();
}
callback(m_ec);
}
This change let the actual openSSL error to show in the console, otherwise it would show a generic "handshake failed" error.
I know I'm not doing what I should with the certificates, but I have no idea where else to look or what to do next. Can anyone here help please? Should I use the .pem files that come with the examples, or should I generate my own? in case I should generate my own, what would be the openSSL command to do that correctly and how do I tell my PC to recognize these as valid so that the server works?
Found the problem: WebSocket++ will not accept a self-signed certificate (the ones you can create directly in your own PC using OpenSSL or the Windows utilities). There is no way around it. You must have a valid, authority-validated and endorsed certificate. You can get such a certificate for free (valid only for 90 days) from https://zerossl.com/. The site has detailed instructions on how to request, obtain and install a certificate. After getting a valid certificate and installing it on my server, everything worked as it should.

openssl 1.0.2j, how to force server to choose ECDH* ciphers

I have client server which uses opensl 1.0.2j, and using RSA:4096 key and certificate and want to force the server to use only the following ciphers.
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-SHA384
ECDH-RSA-AES128-GCM-SHA256
ECDH-RSA-AES128-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES256-SHA256
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES128-SHA256
My server side code will look like below.
method = SSLv23_server_method();
ctx = SSL_CTX_new(method);
SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDH-RSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256");
SSL_CTX_set_ecdh_auto(ctx, 1);
SSL_CTX_set_tmp_dh(ctx, dh);
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
SSL_CTX_use_certificate_file(ctx, certFilePath, SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, privKeyPath, SSL_FILETYPE_PEM)
SSL_accept()
My client side code will look like as below
method = SSLv23_server_method();
ctx = SSL_CTX_new(method);
SSL_CTX_set_cipher_list(ctx, "ECDH-RSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-SHA256");
SSL_CTX_set_ecdh_auto(ctx, 1);
SSL_CTX_set_tmp_dh(ctx, dh);
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
SSL_CTX_use_certificate_file(ctx, certFilePath, SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, privKeyPath, SSL_FILETYPE_PEM)
SSL_connect()
The last step ssl_accept() on the server fails with
err: 336027900 'error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol'
If i use ECDHE*RSA* or DHE*RSA* ciphers on the client side, it is working fine.
Could you please let me know what I am missing?
Edit: server's certificate(certFilePath) contains an RSA public key not the ECDH public key.
Meta: answer only up to TLS1.2; 1.3 no longer has keyexchange and authentication in ciphersuite.
First, it makes no sense to call both set_ecdh_auto and set_tmp_ecdh -- those are mutually exclusive. Also your server doesn't request client authentication, so configuring a self-cert&key on the client is useless. OTOH your server is using a selfsigned cert which probably isn't in the client's default truststore, so you may need to configure the client truststore (there are several approaches to doing that).
Second, 'static' ECDH ciphersuites are quite different from ECDHE suites, just as 'static' DH suites are different from DHE suites. Both static forms are not widely implemented and very little used because they generally offer no benefit; in particular OpenSSL 1.1.0 and up no longer implement them, so your code becomes obsolete in about a year if I remember the schedule correctly.
To be exact, DH-RSA suites require a cert containing a DH key (which permits keyAgreement), and for TLS<=1.1 the cert must be issued by a CA using an RSA key; for 1.2 this latter restriction is removed. No public CA will issue a cert for a DH key, and even doing it yourself isn't easy; see https://security.stackexchange.com/questions/44251/openssl-generate-different-type-of-self-signed-certificate and (my) https://crypto.stackexchange.com/questions/19452/static-dh-static-ecdh-certificate-using-openssl/ .
ECDH-RSA suites similarly require a cert containing an ECC key which permits keyAgreement, and issued by RSA if <=1.1; this is somewhat easier because the key (and CSR) for ECDH is the same as for ECDSA and only KeyUsage needs to differ. For your self-created and self-signed case, it's easy, just generate an ECC key and cert (automatically signed with ECDSA).
But last, this shouldn't cause 'unknown protocol'; it would cause 'no shared cipher' and handshake_failure. The code you've shown shouldn't cause 'unknown protocol', so you probably need to investigate and fix that first. You might try using commandline s_client instead especially with its -debug or -msg hooks, or -trace if you have compiled that in.

ssl version and cipher suites of the client

I'm working on a soap server, that will serve some old embedded computers with an legacy soap protocol.
I write it in go and so far used just plain http, but in production it must use ssl encryption. So I've just created a key and a cert (from this site) and used the http.ListenAndServeTLS function.
But now the computers cannot connect and the server is just printing a handshake error:
server.go:2848: http: TLS handshake error from [::1]:38790: tls: no cipher suite supported by both client and server
In the docs, for the computers, isn't the supported ssl version or the ciphers. So I wanted to know, how to find out the client's ssl version, and also the available cipher suites that the client supports.
And then how can I configure the golang http server so it will support the selected ciphers.
There seems to be two questions here, so let's do this in two parts:
Finding the client's TLS version and supported cipher suites:
To do this, you need to set the GetConfigForClient field of the tls.Config object.
This field takes a method with signature:
func(*ClientHelloInfo) (*Config, error)
It is called on receipt of a Client Hello message with a ClientHelloInfo struct. This struct contains the following fields of interest to you:
// CipherSuites lists the CipherSuites supported by the client (e.g.
// TLS_RSA_WITH_RC4_128_SHA).
CipherSuites []uint16
// SupportedVersions lists the TLS versions supported by the client.
// For TLS versions less than 1.3, this is extrapolated from the max
// version advertised by the client, so values other than the greatest
// might be rejected if used.
SupportedVersions []uint16
Please read the comments around GetConfigForClient and ClientHelloInfo for exactly how GetConfigForClient should behave, and for field details.
Specifying server-supported version and cipher suites:
This is also done through the tls.Config object using the following fields:
// CipherSuites is a list of supported cipher suites. If CipherSuites
// is nil, TLS uses a list of suites supported by the implementation.
CipherSuites []uint16
// MinVersion contains the minimum SSL/TLS version that is acceptable.
// If zero, then TLS 1.0 is taken as the minimum.
MinVersion uint16
// MaxVersion contains the maximum SSL/TLS version that is acceptable.
// If zero, then the maximum version supported by this package is used,
// which is currently TLS 1.2.
MaxVersion uint16
For example, you could set your tls.Config with the following fields:
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
etc...
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
MinVersion: tls.VersionTLS12,
The full list of supported cipher suites is in the tls docs.

Find SSL Version after Handshake in OpenSSL

I want to find out the protocols supported by a target but the problem is that their are quite a number websites which are not supporting a particular version but when i performed handshake it was successful becz target surpassed the version that i gave and perform handshake on the supported version
[ it happened on only 1 website]
example : i passed a version :TLSVersion.TLS_1_2 but the handshake is performed using TLSv1_0 becz it is not supporting TLSVersion.TLS_1_2
Because of the above issue i want to check the version on handshake and i dont want to use scapy.ssl_tls
version = [SSL.SSLv23_METHOD,
SSL.TLSv1_METHOD,
SSL.TLSv1_1_METHOD,
SSL.TLSv1_2_METHOD]
context = OpenSSL.SSL.Context(version)
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.settimeout(CONNECTION_TIMEOUT)
connection = OpenSSL.SSL.Connection(context,soc)
connection.connect((host,port))
connection.do_handshake()
#wants to check version here
i want to check the version on handshake
The relevant functions to check the version both client and server use for the remaining session in pyOpenSSL are get_protocol_version_name or get_protocol_version:
connection.do_handshake()
#wants to check version here
print(connection.get_protocol_version_name())
Note that these functions are only available since pyOpenSSL 0.16.0
Please not also that you cannot specify a list of TLS methods when creating the context but only a single method which essentially specifies the minimal TLS version supported by the client. Thus
context = OpenSSL.SSL.Context(SSL.TLSv1_METHOD)
allows the client to use TLS 1.0 and better. If you instead use SSL.TLSv1_2_METHOD the client would be restricted to TLS 1.2 and better and thus could not establish a SSL connection with a server supporting only TLS 1.0.
Find SSL Version after Handshake in OpenSSL...
If I am parsing what you want correctly... you want the protocol version like printed by openssl s_client:
$ openssl version
OpenSSL 1.1.0b 26 Sep 2016
$ openssl s_client -connect www.google.com:443 -servername www.google.com
CONNECTED(00000005)
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify error:num=20:unable to get local issuer certificate
Server did acknowledge servername extension.
---
...
---
New, TLSv1.2, Cipher is ECDHE-RSA-CHACHA20-POLY1305
Server public key is 2048 bit
Secure Renegotiation IS supported
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
...
The first message of "New, TLSv1.2" tells you about the cipher. That is, when is ECDHE-RSA-CHACHA20-POLY1305 first arrived in TLS. In the case of ECDHE-RSA-CHACHA20-POLY1305, the cipher suite was first seen in TLS 1.2.
The source code for s_client is located at <openssl src>/apps/s_client.c. The code responsible in OpenSSL 1.0.2 is around line 2210:
/* line 2210 */
c = SSL_get_current_cipher(s);
BIO_printf(bio, "%s, Cipher is %s\n",
SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
...
The second message of "Protocol: TLSv1.2" tells you the protocol version used during key exchange and subsequent cipher selection and bulk transfer.
The code responsible in OpenSSL 1.0.2 is <openssl src>/ssl/ssl_txt.c around line 105:
/* line 105 */
int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
{
unsigned int i;
const char *s;
if (x == NULL)
goto err;
if (BIO_puts(bp, "SSL-Session:\n") <= 0)
goto err;
if (x->ssl_version == SSL2_VERSION)
s = "SSLv2";
else if (x->ssl_version == SSL3_VERSION)
s = "SSLv3";
else if (x->ssl_version == TLS1_2_VERSION)
s = "TLSv1.2";
else if (x->ssl_version == TLS1_1_VERSION)
s = "TLSv1.1";
else if (x->ssl_version == TLS1_VERSION)
s = "TLSv1";
else if (x->ssl_version == DTLS1_VERSION)
s = "DTLSv1";
else if (x->ssl_version == DTLS1_2_VERSION)
s = "DTLSv1.2";
else if (x->ssl_version == DTLS1_BAD_VER)
s = "DTLSv1-bad";
else
s = "unknown";
if (BIO_printf(bp, " Protocol : %s\n", s) <= 0)
goto err;
...
}
I want to find out the protocols supported by a target but the problem is that their are quite a number websites which are not supporting a particular version but when i performed handshake ...
This is a different problem. You should look at the source code for sslscan at SSLScan - Fast SSL Scanner to see how it works. The Sourceforge one seems abandoned. It lacks SNI and other new features, like secure negotiation and ALPN.
You might try this sslscan from GitHub: rbsec/sslscan. The GitHub one is actively maintained and seems to be more up to date.
example : i passed a version :TLSVersion.TLS_1_2 but the handshake is performed using TLSv1_0 becz it is not supporting TLSVersion.TLS_1_2
This will not happen. TLS specifies one protocol version only. The idea is your try TLS 1.2. If it fails, then you fall back to TLS 1.1. If it fails, then you fall back to TLS 1.0. Ad infinitum.
The try-and-fallback approach is the reason for RFC 7504, TLS Fallback Signaling Cipher Suite Value (SCSV) for Preventing Protocol Downgrade Attacks. This was an awful band-aide from the Browser crowd. See, for example, Last Call: <draft-ietf-tls-downgrade-scsv-03.txt> (TLS Fallback Signaling Cipher Suite Value (SCSV) for Preventing Protocol Downgrade Attacks) to Proposed Standard.
TLS does not accept a range of protocol versions like many folks think. We have tried to get it changed a few times. See, for example, A new TLS version negotiation mechanism.

Not possible to accept only TLS 1.2 with boost::asio?

So I am trying to modify a third party (libtorrent) to only accept the TLS 1.2 protocol.
Part of the setup of the SSL context:
boost::shared_ptr<context> ctx = boost::make_shared<context>(boost::ref(m_ses.get_io_service()), context::tlsv12)
ctx->set_options(context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
| boost::asio::ssl::context::no_tlsv1
| boost::asio::ssl::context::no_tlsv1_1
| boost::asio::ssl::context::single_dh_use);
However when I am testing my connection with OpenSSL s_client it still seems to accept tls 1.0 and tls 1.1 connection.
Is there something I am doing wrong?
EDIT: Added "| boost::asio::ssl::context::no_tlsv1_1" to options. I realized I was referring to an old boost reference guide. It did however not change anything.
EDIT: I just realize that I have not mentioned that this connection is a two-way/mutual authentication connection. Not sure if that changes anything.
There is no constant for TLS1.2 in asio::ssl::context. But you can use native openssl API to do that:
#include <openssl/ssl.h>
long ssl_disallowed = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
ssl_disallowed &= ~SSL_OP_NO_TLSv1_2;
SSL_CTX_set_options(ctx.native_handle(), ssl_disallowed);
This is a total shot in the dark, but try this:
Try creating a string of ciphers specific to TLS 1.2 and then call
char* TLS_12_CIPHERS = "... list of ciphers specific to TLS 1.2";
SSL_CTX_set_cipher_list(ctx->native_handle(), TLS_12_CIPHERS);
Then set the option on the context (assuming it's a server context) that the server gets to choose what ciphers it wants use, not client.
SSL_CTX_set_options(ctx->native_handle(), SSL_OP_CIPHER_SERVER_PREFERENCE);
You'd think that boost::asio::ssl would take care of this stuff for you by specifying the no_X options but I can't be sure. Like I said this is a shot in the dark, but explicitly configuring context using the OpenSSL API in this way should enforce the conditions you're after. Even if somewhere, somehow, some conflicting option is being set to allow non TLS 1.2 connections, with these options, any non TLS 1.2 connection will fail with the error "no shared cipher".
As for why your server is even advertising that non 1.2 connections are acceptable is unknown, but one possible explanation is that there is a default context that is advertising this. This is why sehe made the point about "applying to all connections."
Here is a list of TLS 1.2 specific ciphers.