SSLServerSocket and keystores
◈ 5 cardsWhy the default server factory is not enough, what a keystore holds, building an SSLContext from KeyStore and KeyManagerFactory, self-signed certificates, and demanding client authentication.
The factory again — and why the default one is not enough
The server side mirrors the client side exactly on the surface. SSLServerSocket extends java.net.ServerSocket, its constructors are protected, and you obtain instances from SSLServerSocketFactory:
SSLServerSocketFactory factory =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
ServerSocket server = factory.createServerSocket(7000);
The three createServerSocket overloads mirror the ServerSocket constructors — port, port plus backlog queue length, port plus backlog plus a local interface to bind to.
Then reality intervenes. The factory that getDefault() returns generally cannot encrypt, because a server needs something a client does not: its own private key and a certificate for it. There is nothing for getDefault() to load, so a real TLS server has to configure the material itself.
What a keystore holds
That material lives in a keystore — an encrypted file holding key entries and certificate entries, each under an alias. A server needs one key entry: its private key plus the certificate chain that vouches for the matching public key. The whole file is protected by a passphrase, and in Java that passphrase is a char[] rather than a String, deliberately. A String is immutable and sits in the heap until the garbage collector feels like reclaiming it; a char[] can be overwritten the instant you are done with it, which is why Arrays.fill(password, '0') is idiomatic rather than paranoid.
A truststore is the same file format used for the opposite purpose: certificates you are willing to trust as signers. The JDK ships one preloaded with the public CA roots, which is what let last lesson's client verify a chain with no configuration at all.
keytool, and the self-signed certificate problem
keytool ships with the JDK and creates the file:
keytool -genkeypair -alias server -keyalg RSA -keysize 2048 \
-keystore server.p12 -storetype PKCS12 -validity 365
It prompts for a passphrase and for the distinguished-name fields, generates a key pair, and stores the private key with a self-signed certificate — a certificate whose signature was made by the very key it certifies.
That certificate is cryptographically perfect and socially worthless. Nothing signed it except itself, so no client's truststore contains a root that leads to it, and every default client rejects it. To be trusted publicly you send a certificate signing request to a CA, prove you control the name, and import what they send back. For coursework and internal testing you can instead import the self-signed certificate into the client's truststore explicitly — which is a fine thing to do knowingly and a terrible thing to do by disabling verification.
Wiring the SSLContext
An SSLContext is the object that holds the configured key material and manufactures configured factories from it. The chain is five steps: load a KeyStore, initialise a KeyManagerFactory with it, initialise an SSLContext with that factory's key managers, ask the context for an SSLServerSocketFactory, and create the server socket. Pass null for the trust managers and the randomness source to accept the defaults — which for a server that does not check client certificates is exactly right.
There is a lighter route worth knowing for a demo: set the system properties javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword on the command line and the default factory will find your keystore, no SSLContext code at all. It is convenient and it puts the passphrase into the process table and your shell history, so use it for a scratch server and never for anything else.
Mutual authentication
By default the server proves its identity and the client proves nothing — the asymmetry the whole web runs on, because making every visitor obtain a certificate would be intolerable. Where both ends are under your control, setNeedClientAuth(true) on the SSLServerSocket inverts that: connections whose client cannot present an acceptable certificate are refused. The related setUseClientMode(boolean) decides which side of the handshake this socket plays, and can be set only once per socket — a second call throws IllegalArgumentException.
The cipher-suite methods repeat here too, with one difference in scope: setEnabledCipherSuites on an SSLServerSocket sets the default for every socket it accepts, while the same method on an individual accepted SSLSocket narrows just that one.
Worked example — a TLS order taker on port 7000
Take the thread-pooled server from Module 7 and change how the listening socket is born; nothing else moves.
Read the passphrase with System.console().readPassword() so it never appears on the command line. Load server.p12 as a PKCS12 KeyStore, initialise a KeyManagerFactory with the store and the same passphrase, create an SSLContext for TLS, and initialise it with kmf.getKeyManagers() and two nulls. Overwrite the password array immediately.
Ask the context for its SSLServerSocketFactory, create the SSLServerSocket on 7000, and — because this server takes orders from a handful of known partner systems rather than the public — call setNeedClientAuth(true).
The accept loop is then character-for-character the Module 7 loop: accept() in a while (true), hand the returned Socket to an ExecutorService, and let the handler read lines from a BufferedReader and write a reply. The handler does not know it is encrypted, does not import anything from javax.net.ssl, and would work unmodified over a plain ServerSocket. All the security lives in the eight lines that built the socket.