Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conceptual overview of server-side SSL in Java [closed]

My task is to secure a (previously HTTP) web service with HTTPS. From a now departed coworker I have inherited code that inserts an SSLEngine object between the TCP and HTTP layers in our existing server. As far as I know this code works correctly. I get the SSLEngine from SSLContext.createSSLEngine(), but how to produce an appropriate SSLContext confuses me.

SSLEngine itself has a beautiful conceptual introduction in its javadoc, but unfortunately is the part that I don't need to interface to myself. On the other hand SSLContext.init() is very sparsely documented and just say that I must pass "the sources of authentication keys" and "the sources of peer authentication trust decisions", and I have no idea what that is. The documentation for the types of these parameters (which would ordinarily my next try for understanding it) are generic to the point of not saying anything, and the class documentation for SSLContext is also uselessly brief.

I am provided with a bunch of ascii-armored .crt, .pem, and .key files that together enable Apache to serve HTTPS at the domain the Java server is eventually going to handle directly. I suppose I need to load them into either the SSLContext or the SSLEngine somehow, but am not sure whether SSLContext.init() is even the right place to to that (though there doesn't seem to be many other places it could be).

Which documentation should I start by reading to get a working understanding of how to do this?

My Google attempts produce lots of semi-undocumented example code of unknown quality and security, as well as some advanced walk-throughs such as "how to write your own key provider", but no overall conceptual introduction to the most basic use of the JRE classes.

Especially since this is security related, I have no use for copy-paste example code that I'll just whack on aimlessly until it seems to do more-or-less what I want. I need a high-level conceptual understanding of how the various pieces are actually supposed to fit together.

(Bonus points if the documentation is detailed enough to let me figure out how to do SSL client authorization in practice too -- but that is not immediately urgent).

like image 661
hmakholm left over Monica Avatar asked Oct 07 '22 22:10

hmakholm left over Monica


1 Answers

If you want detailed documenntation, take a look at the JSSE Reference Guide, more specifically its SSLContext section.

The default values (if you pass null to SSLContext.init(...)) are sensible by default, but you may want to understand what these default values are (see the Customization section).

There's no default for the keystore (only the truststore, which you'll almost certainly want to customise anyway if you want client certificate authentication).

Typically, you can initialise an SSLContext as follows:

KeyStore ks = KeyStore.getInstance(...); // Load the keystore
ks.load(...); // Load as required from the inputstream of your choice, for example.

KeyStore ts = KeyStore.getInstance(...); // Load the truststore
ts.load(...);

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, <the key password>);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

Perhaps you may also be interested in this answer for the difference between keystore and truststore. In short, both are "keystores" in the storage sense, but the keystore is where you store your certificates (+chain) and associated private keys (i.e. the server cert and priv. key on a server and the client cert and priv. key on a client) and the truststore is where you store the CA certificates or the remote certificates (without private keys, because they're not your certs) that you're willing to trust, when the remote party presents its certificate.

Regarding what to do with your key/cert files, the easiest is certainly to use a keystore of type PCKS12, which you can build as described in this answer.

EDIT: (Following comments)

So is it correctly understood that I can get away with a null TrustManager if I'm a server and not yet doing SSL client authentication?

Yes.

But that if I were to do cliemt authentication I need to provide a TrustManager that contains the public keys of every client that should be able to connect?

Usually not. You'd provide a CA certificate for the CA that issued these client certificates. If you don't have this infrastructure (or if you're building it and don't have any client certificates yet), you should look into creating your own PKI, or perhaps buying client certificates from a well-known CA.

You could also build a TrustManager that accepts self-signed certificates (independently from any CA) and verifies them using the fingerprint from a pre-defined list, but this comes with a number of problems (in particular how the server asks for the right certificate), and you'd may end up replicating parts of what PKIs are for. That's not something I'd recommend unless you understand more about what you're doing.

That's a bit of a downer; I had hoped I could have waited until I got the request URL before I needed to retrieve a fingerprint for the authorized client and only then compare to the fingerprint the actual client authenticated with.

Here, you're talking about a completely different aspect. If you want to get the requested URL first, before requesting the certificate, you'll need to use re-negotiation. The HTTP layer would have to talk to the SSLEngine for ask it to trigger a new handshake (now set up to ask for a client certificate).

SSLEngine isn't the easy path to SSL/TLS in Java in general, but asynchronous renegotiation can become very tricky. Indeed, its semantics aren't quite clear at the application layer. You could very well trigger a re-negotiation after receiving an HTTP request, but be in the middle of sending a response at the same time (since you may have asynchronous request/response, possibly pipelined). Technically, the new handshake would affect both pipes.

Overall, renegotiation or not, that's quite independent of checking how you trust the client certificate. If you really want to go down the road of having your application layer (and not the SSL/TLS layer) do the certificate verification, you'd have to write an X509TrustManager that trusts anything (bypassing the verification at the SSL/TLS layer) and have your application get the cert from the SSLSession (peer certificate) and do the verification there. Overall, that's quite similar to accepting self-signed certs and verifying them more manually. It can be done, but you'd be stepping out of the PKI model, and you would need some custom code to do so (as opposed to just using the API as it's meant to be used by default).

If you're new to all this, I'd avoid this approach. Try perhaps to set up a test CA and learn how it works first. The whole issue about using a CA or doing manual fingerprint verification is ultimately more an administrative problem: it's defined by how you're going to distribute your certificates between the parties involved.

Also, what is <the key password> doing here? This is supposed to run on an unattended server in a hosting facility somewhere; we cannot wait for someone to come around to type in a password during startup (say, after a power outage).

You'll need to set a password. Most servers read it from a configuration file if needed (or worse, you could hard-code it).

like image 153
Bruno Avatar answered Oct 22 '22 07:10

Bruno