c++ thrift client not working with ssl (SSL_connect hangs) - c++

I wanted to use ssl support provided by thrift in my c++ server and client. My own thrift client was always hanging in SSL_connect after it passes through "transport->open()"
. So I built the official thrift\thrift-0.9.1\test\cpp\src\TestServer.cpp and testclient.cpp for windows. Same thing happened to me here as well.
I really could use any help or pointers.
Update:
I also tried using the latest sources at https://github.com/apache/thrift
Before I was working with 0.9.1
Since I saw testserver.cpp was doing the following
sslSocketFactory->loadCertificate("./server-certificate.pem");
sslSocketFactory->loadPrivateKey("./server-private-key.pem");
sslSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH");
and the testclient.cpp was doing the following
factory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH");
factory->loadTrustedCertificates("./trusted-ca-certificate.pem");
factory->authenticate(true);
So I took following steps to build the certs
openssl genrsa -out ca-private-key.pem 2048
openssl req -new -x509 -nodes -days 3600 -key ca-private-key.pem -out ca-certificate.pem
openssl req -newkey rsa:2048 -days 3600 -nodes -keyout server-private-key.pem -out server-request.pem
openssl rsa -in server-private-key.pem -out server-private-key.pem
openssl x509 -req -in server-request.pem -days 3600 -CA ca-certificate.pem -CAkey ca-private-key.pem -set_serial 01 -out server-certificate.pem
Output for different test cases-
TestServer.exe --ssl
TestClient.exe --host 192.168.0.4 --ssl
I saw TestClient.exe hang on SSL_connect while running
testClient.testVoid();
During hang server side callstack
During hang client side callstack. Clearly both sides are stuck reading!
Wireshark debug trace for the above mentioned client server communication.
Debug output through "openssl s_client" run against thriftserver-
openssl s_client -connect 192.168.0.4:9090 -state -debug
Loading 'screen' into random state - done
CONNECTED(00000100)
SSL_connect:before/connect initialization
write to 0x1e2b5c0 [0x1e2bf50] (321 bytes => 321 (0x141))
0000 - 16 03 01 01 3c 01 00 01-38 03 03 52 dc 25 39 ad ....<...8..R.%9.
SSL_connect:SSLv2/v3 write client hello A
TestServer.exe --ssl --server-type nonblocking
TestClient.exe --ssl
I saw TestClient.exe failed on SSL_connect (10054) while running
testClient.testVoid();
Server stderr was saying
Thrift: Sat Jan 18 19:31:21 2014 TNonblockingServer: frame size too large (369295616 > 268435456)
from client <Host: ::1 Port: 22869>. Remote side not using TFramedTransport?
openssl.exe s_client -connect localhost:9090 -state -debug
Loading 'screen' into random state - done
CONNECTED(0000018C)
SSL_connect:before/connect initialization
write to 0x6db5c0 [0x6dbf50] (321 bytes => 321 (0x141))
0000 - 16 03 01 01 3c 01 00 01-38 03 03 52 db 4b 8a dd ....<...8..R.K..
SSL_connect:SSLv2/v3 write client hello A
read from 0x6db5c0 [0x6e14b0] (7 bytes => -1 (0xFFFFFFFF))
SSL_connect:error in SSLv2/v3 read server hello A
write:errno=10054
TestServer.exe --ssl --server-type nonblocking --transport framed
TestClient.exe --ssl --transport framed
Server stderr was saying
Thrift: Sat Jan 18 19:36:01 2014 TNonblockingServer: frame size too large (36929
5616 > 268435456) from client <Host: ::1 Port: 23087>. Remote side not using TFramedTransport?
By stepping through I definitely confirmed that testclient was using framed transport.

I think I know what may be going on and might have discovered the bug.
After debugging further I saw that virtual function createSocket is declared with parameter "int"
boost::shared_ptr createSocket(int socket);
https://github.com/apache/thrift/blob/master/lib/cpp/src/thrift/transport/TSSLServerSocket.h
https://github.com/apache/thrift/blob/master/lib/cpp/src/thrift/transport/TSSLServerSocket.cpp
However the base class TServerSocket.h declare it as "THRIFT_SOCKET" which on windows is ULONG_PTR
virtual boost::shared_ptr createSocket(THRIFT_SOCKET client);
https://github.com/apache/thrift/blob/master/lib/cpp/src/thrift/transport/TServerSocket.h
Hence correct createSocket was not being called from the guts.
After making this change I am able to move forward which I confirmed again with openssl s_client -connect localhost:9090 -state -debug
I will send my patch to thrift dev in case they would like to accept it.

Related

"unknown ca" with self-generated CA, certificates and client/server

I'm writing a custom client & server that I want to communicate securely over the public Internet, therefore I want to use OpenSSL and have both ends do peer verification to ensure that my client isn't mis-directed by a MITM, and likewise that an unauthorized client isn't able to connect to the server.
This is the error received from the server during the SSL_connect / SSL_accept phase:
15620:error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:ssl\record\rec_layer_s3.c:1528:SSL alert number 48
I'm running under Windows 10, using OpenSSL 1.1.1. I'm using the following batch file to create them. I enter the ca private key passphrase by hand for obvious reasons.
openssl genrsa -out -des3 ca.key.pem 2048
openssl genrsa -out server.key.pem 2048
openssl genrsa -out client.key.pem 2048
openssl req -x509 -new -nodes -key ca.key.pem -sha256 -days 365 -out ca.cert.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=Foobar
openssl req -new -sha256 -key server.key.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=Foobar -out server.csr
openssl x509 -req -in server.csr -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -out server.cert.pem -days 365 -sha256
openssl req -new -sha256 -key client.key.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=Foobar -out client.csr
openssl x509 -req -in client.csr -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -out client.cert.pem -days 365 -sha256
The intent here is to create a self-signed CA, and then have that directly sign both the client and server keys.
ca.key.pem will be stored in a secure place: on an encrypted veracrypt volume.
Both client and server use the following call to enable peer verification:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
I'm fairly certain this is a certificate issue because the errors go away if I remove that line.
Answering this myself so that it can help anyone else that might arrive here looking for solutions to this problem. The answer was found in another SO question, but is worth repeating here: The Common Name for the CA cannot be the same as the Common Name for the client and server certificates.
So changing the fourth line of the batch file to this:
openssl req -x509 -new -nodes -key ca.key.pem -sha256 -days 365 -out ca.cert.pem -subj /C=US/ST=CA/L=Somewhere/O=Someone/CN=FoobarCA
fixed the problem.
$ openssl req -x509 -new ... -addext basicConstraints=critical,CA:TRUE
This essentially creates a certificate which has 2 basic contrains CA:TRUE extensions:
$ openssl x509 -in ca.cert.pem -text
X509v3 extensions:
...
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Basic Constraints: critical
CA:TRUE
Trying to use the CA to verify the server certificate will not work:
$ openssl verify -CAfile ca.cert.pem server.cert.pem
C = XX, ST = XX, L = XX, O = XX, CN = CA
error 24 at 1 depth lookup: invalid CA certificate
error server.cert.pem: verification failed
Given that this simple check does not work, the client will also not be able to validate the server certificate, resulting in an unknown ca alert:
...:tlsv1 alert unknown ca:...
When skipping the -addext it will create a self-signed certificate as documented, which already has CA:TRUE
$ openssl req -x509 -new ...
...
$ openssl x509 -in ca.cert.pem -text
X509v3 extensions:
...
X509v3 Basic Constraints: critical
CA:TRUE
And using this to verify the server certificate works:
$ openssl verify -CAfile ca.cert.pem server.cert.pem
server.cert.pem: OK
This certificate should also be successfully validated by your client, thus no longer resulting in unknown ca.

AWS EC2 FTPS set up

I can connect to my server using normal FTP but not on FTPs (I believe I need a certificate) I have opened ports on the server:
22 ssh
20 - 21 tcp
50000 - 51000 tcp
tcp 990
I am using EC2, I am using vsftpd and set the .conf file like I think I ought to (I followed silicon dales tutorial).
Could anyone point me the right direction? Like how to get a certificate and where to put it etc and whether the .conf needs to be updated?
The error I currently get when trying to connect to port 990 from Filezilla is:
Connection attempt failed with "ECONNREFUSED - Connection refused by server".
It could be your SecurityGroups.
Based on this DigitalOcean post, it looks like you have to open port range 40000-50000 for vsftpd.
Issue solved: I did it by using these commands on the server:
mkdir -p /etc/vsftpd/ssl
cd /etc/vsftpd/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout vsftpd-private.pem -out vsftpd-cert.pem
I also edited the vsftpd.conf file adding:
one_process_model=no
ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO
ssl_ciphers=HIGH
rsa_cert_file=/etc/vsftpd/ssl/vsftpd-cert.pem
rsa_private_key_file=/etc/vsftpd/ssl/vsftpd-private.pem

Create OpenSSL certificates signed by myself

I'm using boost ssl for server and client, and I have a model for server/client program in my mind, and I'm not sure it's gonna work.
The model I have in my mind is to be the only authority for certificates of my program. My main question is: How can I do that?
In my server program, I define keys as follows:
context_.use_certificate_chain_file("../sslkeys/server.crt");
context_.use_private_key_file("../sslkeys/server.key", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("../sslkeys/dh512.pem");
I create/sign those keys/certificates using:
$ openssl genrsa -des3 -out server.key 2048
$ openssl req -new -key server.key -out server.csr
$ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
$ cp server.key server.key.secure
$ openssl rsa -in server.key.secure -out server.key
$ openssl dhparam -out dh512.pem 512
For my client program, I would like to create a certificate and sign it by my "server.key", because I think (and I could be wrong, please correct me if I'm) that's the way to do it. The client program requires a key using the command:
ctx.load_verify_file("../sslkeys/client.csr");
So I created a key, which I signed using the server key, with the following commands:
$ openssl genrsa -des3 -out client.key 2048
$ openssl req -new -key client.key -out client.csr
$ openssl x509 -req -days 3650 -in client.csr -signkey ../sslkeys/server.key -out client.crt
Now when I run my client and try to connect the server, I get the error: Handshake failed: certificate verify failed
What is wrong in what I'm doing? And how can I achieve the model I mentioned?
If you require any additional information, please ask.
Thanks for any efforts.
Your signing certificate has no rights to sign, because it has not the CA flag set. Signing will still work, but verification will fail. Since there are already lots of guides on the internet which will show in detail how to do it right so you might just look here or here for more details.
Also, using only a 512 bit Diffie-Hellman reduces the security of the key exchange to 512 bit, which is exploitable today (see also Logjam attack). The 2048 RSA key does not help here. And using 512 bit might not even work if you use the latest version of OpenSSL which just increased the minimal size to 768 bits for security reasons.

how to use ssl certificates in openssl with gsoap, using a c++ client/server

I am using gsoap and openssl under Visual Studio C++, I created a client and a server on localhost (port 443).
I have a non explicit error without any description when using (from client side) the option:
soap_ssl_client_context(&soap, "SOAP_SSL_DEFAULT"...
but if I use it with the option:
soap_ssl_client_context(&soap, SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK, ...
it is working correctly (but insecurely I gess).
So I decided to check what is the error by checking packets on localhost (with raw capture), and I see that the communication ends by an Encrypted Alert (21) after terminating the handshake.
And I Wonder what I need to do, to get this application working properly and securely on localhost (for testing purpose).
More Info:
I have generated ssl certificates for server side with a batch:
echo CREATE SERVER CA and CA CERT
echo Generate Private Key (passwd protected)
openssl genrsa -des3 -out .\private\CA_key.pem 2048
pause
echo Generate server CA
echo use your server name for the 'common name' field!
openssl req -out ca.pem -new -x509 -key .\private\CA_key.pem
pause
echo Create certificate signing request for CA pub Key
openssl req -new -key .\private\CA_key.pem -out CA_csr.pem
pause
echo Sign it
openssl req -in CA_csr.pem -out CA_crt.pem -key .\private\CA_key.pem -x509 -days 3020
pause
echo FOR C++ SERVER ONLY
type .\private\CA_key.pem CA_crt.pem > server.pem
pause
And also for client side:
echo CREATE PUB/PRIV key pair and cert for client
echo Generate key pair
openssl genrsa -des3 -out client_key.pem 2048
pause
echo Create CSR for client pub key
openssl req -new -key client_key.pem -out client_csr.pem
pause
echo User ca to sign the request (need serial file with '01')
echo make sure your openssl.cnf is correct (path and right CA certificate file)
openssl ca -in client_csr.pem -out client_crt.pem -config openssl.cfg -days 1825
pause
echo CLIENT SPECIFIC FORMATING (optional)
echo for C++ clients ONLY
type client_key.pem client_crt.pem > LCC.pem
I used:
CA_crt.pem as "cacert file" in both soap_ssl_server_context and soap_ssl_client_context.
LCC.pem as client key, and server.pem as server key.
I am not sure if all certificate generation steps are correct but it is working with the option (SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK).
Can you help me please to find out what is missing to get it working with SOAP_SSL_DEFAULT only ?
Thank you
I am not sure if all certificate generation steps are correct but it is working with the option (SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK).
If the certificate works with gSoap with SOAP_SSL_SKIP_HOST_CHECK and does not work without it, then the CommonName for your certificate is not a hostname or IP address. Depending on the purpose for your certificate, you may not want your certificate CommonName to be the host ip/name and so using SOAP_SSL_SKIP_HOST_CHECK is fine.
If you want to quit using the SOAP_SSL_SKIP_HOST_CHECK flag, then regenerate your certificate to have the CommonName be the host name or ip address. (Note: You may run into conflicts with other certificates installed on your system - if one of them has an identical CommonName.)

Exception running boost asio ssl example

I'm trying to run the SSL examples from boost::asio and I'm getting an "Invalid argument" exception when I run them. I'm on Linux x86_64.
http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/example/ssl/client.cpp
http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/example/ssl/server.cpp
Compiled with:
g++ server.cpp -o server -lboost_system -lssl
g++ client.cpp -o client -lboost_system -lssl
Run like:
$ ./server
Usage: server <port>
$ ./server 10000
Exception: Invalid argument
$ ./server 1000
Exception: Permission denied
$ sudo ./server 1000
Exception: Invalid argument
Not sure what the problem is :(
Any help would be greatly appreciated.
Thanks!
OK, for anyone finding this in the future, you need to create your certificates and sign them appropriately.
Here are the commands for linux:
//Generate a private key
openssl genrsa -des3 -out server.key 1024
//Generate Certificate signing request
openssl req -new -key server.key -out server.csr
//Sign certificate with private key
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
//Remove password requirement (needed for example)
cp server.key server.key.secure
openssl rsa -in server.key.secure -out server.key
//Generate dhparam file
openssl dhparam -out dh512.pem 512
Once you've done that, you need to change the filenames in server.cpp and client.cpp.
server.cpp
context_.use_certificate_chain_file("server.crt");
context_.use_private_key_file("server.key", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("dh512.pem");
client.cpp
ctx.load_verify_file("server.crt");
Then it should all work!
Execute the tests again with strace to see which syscall gets the EINVAL, as a bonus you'll get to see the args for the failing call. It's likely part of the security context setup that's failing, unless you have the right files and data from the example:
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("dh512.pem");
You were getting EPERM because you were trying to bind to a privileged TCP port (one whose value is less than 1024). That's why ./server 10000 does not get EPERM.
When following the answer of #Shootfast an error appered: 'bad SSL configuration: use_certificate_chain_file: ee key too small'
Changing the first line:
openssl genrsa -des3 -out server.key 1024
to:
openssl genrsa -des3 -out server.key 2048
fixed it for me.
After that I got the error: 'bad SSL configuration: use_private_key_file: no start line' the reason and solution to this is explained here: solution (It is more or less the reason for the last command of #Shootfast answer.)