This is how I generate my SSL cert, key, etc:
openssl genrsa -out server.key 1024
openssl rsa -in server.key -out new_key.pem
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 10000 -in server.csr -signkey new_key.pem -out server.crt
This works, i can see the output in chrome, although I get a warning that i'm going to get viruses first.
openssl s_server -cert server.crt -www -key new_key.pem
this is a snippet from the server. I'll be honest, i'm not sure exactly what every line is doing, though I have a good idea:
socketFactory->server(true); // this is the server
socketFactory->authenticate(false); // no auth?
socketFactory->loadCertificate("server.crt");
socketFactory->loadPrivateKey("new_key.pem");
client:
socketFactory->loadTrustedCertificates("server.crt");
socketFactory->authenticate(true); //auth? wierd, right? This guy does this:[1]
[1] http://permalink.gmane.org/gmane.comp.lib.thrift.user/1651
If i comment out loadTrustedCertificates
in the client, then i get an SSL unverified certificate exception.
With that line left in, i get an auth failure exception.
Here are 2 much longer code fragments, that put the above snippets in better perspective.
server:
shared_ptr<SkullduggeryHandler> handler(new SkullduggeryHandler());
shared_ptr<TBufferedTransportFactory> transportFactory =
shared_ptr<TBufferedTransportFactory>(new TBufferedTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<TProcessor> processor(new SkullduggeryProcessor(handler));
shared_ptr<TSSLSocketFactory> socketFactory =
shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory());
socketFactory->server(true);
socketFactory->authenticate(false);
socketFactory->loadCertificate("server.crt");
socketFactory->loadPrivateKey("new_key.pem");
shared_ptr<TSSLServerSocket> socket(new TSSLServerSocket(port, socketFactory));
TThreadedServer server(processor,
socket,
transportFactory,
protocolFactory);
server.serve();
client:
shared_ptr <TSSLSocketFactory> socketFactory = shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory());
socketFactory->loadTrustedCertificates("server.crt");
socketFactory->authenticate(false);
shared_ptr <TSSLSocket>socket = socketFactory->createSocket(configuration.ip, configuration.port);
shared_ptr<TBufferedTransport> transport(new TBufferedTransport(socket));
shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
SkullduggeryClient client(protocol);
transport->open();
Thanks for taking the time to read this. If there are glaring errors, I'll be glad to hear of it. This has been the bane of my existense for too long now. Too long.
It seems you are generating self-signed certificates (which is fine), but the operations you are making with the openssl
utility are confusing.
Line 1 is OK, it generates a private key.
Line 2 is useless: the output key is the same as the input key! (Try to diff
the two keys to see).
Line 3 generates a CSR and line 4 actually self-signs it, so they can be merged together in one line as we will see.
Now, let's take a step back and let's try to understand what we are doing :-)
You are using SSL to authenticate and encrypt the communication between a Thrift server and a Thrift client. I assume you want to both:
To make an HTTPS analogy, (1) is the classic server certificate, (2) is normally a username/password for the user. But with Thrift SSL, we will obtain mutual authentication by issuing a certificate also to the client.
The examples I will make will use self-signed certificates. They can easily be adapted to a mini CA managed by openssl, and I leave this as an exercise to the reader.
Generate the server private key:
openssl genrsa -out server-key.pem 2048
Generate the associated public key and self-sign it:
openssl req -new -x509 -key server-key.pem -out server-cert.pem -days 10000
Generate the client private key:
openssl genrsa -out client-key.pem 2048
Generate the associated public key and self-sign it:
openssl req -new -x509 -key client-key.pem -out client-cert.pem -days 10000
Note: when openssl req
asks for "Common Name (e.g. server FQDN or YOUR name)"
, put the FQDN of the host on which the Thrift program will run. This will allow not to customize Thrift's AccessManager
class. If on the other hand the FQDN can not be known in advance, one needs to inherit AccessManager
and override the verify()
methods accordingly. See TSSLSocket.cpp
.
Good, now to the code.
On the server side:
socketFactory->server(true);
is redundant, remove it.
socketFactory->authenticate(false)
is a bit misleading. A better name would have been authenticatePeer
. If you say false
, it will not authenticate the client, but we decided before we want mutual authentication.
So, a SSL preamble for a server is:
try {
signal(SIGPIPE, SIG_IGN); // See README.SSL
shared_ptr<TSSLSocketFactory> sslSocketFactory(new TSSLSocketFactory());
sslSocketFactory->loadPrivateKey(myKey);
sslSocketFactory->loadCertificate(myCert);
sslSocketFactory->authenticate(true);
sslSocketFactory->loadTrustedCertificates(trustedCerts);
sslSocketFactory->ciphers("HIGH:!DSS:!aNULL@STRENGTH");
...
} catch (TException& tx) {
....
}
Where myKey
is server-key.pem
, myCert
is server-cert.pem
and trustedCerts
is... either the cert of a trusted CA, or, in case of a self-signed cert, the cert of the client. You can cat
multiple certs one after the other in the same file. In our example, we will put client-cert.pem
that we created before.
A SSL preamble for a client is exactly the same, with the correct client private key, client cert and, for trustedCerts
, the cert of the peer: server-cert.pem
that we created before.
That's all :-) Try to understand before jumping to code it, if you don't have a clear picture of how SSL (mutual) authentication works, it is difficult to understand the error messages. The code I showed is tested to work.
Documentation-wise, unfortunately Thrift is close to nothing. For SSL, you can see: lib/cpp/README.SSL
, test/cpp/src/TestServer.cpp
and test/cpp/src/TestClient.cpp
. Be warned that TestServer.cpp
doesn't do mutual authentication, which is an error IMHO.