I have been tasked to implement a custom/standalone Java webserver that can process SSL and non-SSL messages on the same port.
I have implemented an NIO server and its working quite well for non-SSL requests. I am having a heck of a time with the SSL piece and could really use some guidance.
Here's what I have done so far.
In order to distinguish between SSL and non-SSL messages, I check the first byte of the inbound request to see if it is a SSL/TLS message. Example:
byte a = read(buf);
if (totalBytesRead==1 && (a>19 && a<25)){
parseTLS(buf);
}
In the parseTLS() method I instantiate an SSLEngine like this:
java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS");
java.security.KeyStore ts = java.security.KeyStore.getInstance("JKS");
ks.load(new java.io.FileInputStream(keyStoreFile), passphrase);
ts.load(new java.io.FileInputStream(trustStoreFile), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine serverEngine = sslc.createSSLEngine();
serverEngine.setUseClientMode(false);
serverEngine.setEnableSessionCreation(true);
serverEngine.setWantClientAuth(true);
Once the SSLEngine is instantiated, I process the inbound data using the unwrap/wrap methods using code straight out of the official JSSE Samples:
log("----");
serverResult = serverEngine.unwrap(inNetData, inAppData);
log("server unwrap: ", serverResult);
runDelegatedTasks(serverResult, serverEngine);
log("----");
serverResult = serverEngine.wrap(outAppData, outNetData);
log("server wrap: ", serverResult);
runDelegatedTasks(serverResult, serverEngine);
The first part of the handshake seems to work just fine. The client sends a handshake message and the server responds with a message with 4 records:
handshake (22)
- server_hello (2)
- certificate (11)
- server_key_exchange (12)
- certificate_request (13)
- server_hello_done (14)
Next, the client sends a message with three parts:
handshake (22)
- certificate (11)
- client_key_exchange (16)
change_cipher_spec (20)
- client_hello (1)
handshake (22)
*** Encrypted Message ****
The SSLEngine unwraps the client request and parses the records but the wrap method produces 0 bytes with a handshake status of OK/NEED_UNWRAP. In other words, there's nothing for me to send back to the client and the handshake comes to a screeching halt.
This is where I am stuck.
In the debugger, I can see that the SSLEngine, specifically the ServerHandshaker, doesn't find any peer certs. This is rather obvious when I look at the certificate record from the client which is 0 bytes long. But why?
I can only assume that there's something wrong with the HelloServer response but I can't seem to put my finger on it. The server seems to be sending a valid cert but the client isn't sending anything back. Is there a problem with my keystore? Or is it the truststore? Or does it have something to do with the way I'm instantiating the SSLEngine? I'm stumped.
Couple other points:
I look forward to any guidance you might have but please don't tell me I'm nuts or to use Netty or Grizzly or some other existing solution. Its just not an option at this time. I just want to understand what I'm doing wrong.
Thanks in Advance!
It enables TLS connections to virtual servers, in which multiple servers for different network names are hosted at a single underlying network address. If you disable jsse.enableSNIExtension you won't be able to connect to pages under a virtual server.
The handshake allows the server to authenticate itself to the client by using public-key techniques, and then allows the client and the server to cooperate in the creation of symmetric keys used for rapid encryption, decryption, and tamper detection.
The SSLHandshakeException indicates that a self-signed certificate was returned by the client that is not trusted as it cannot be found in the truststore or keystore . This SSLException is seen on the client side of the connection. sun. security.
You got NEED_UNWRAP, so do an unwrap. That in turn might give you BUFFER_UNDERFLOW, which means you have to do a read and retry the unwrap.
Similarly when you get NEED_WRAP, do a wrap: that in turn might give you BUFFER_OVERFLOW, which means you have to do a write and retry the wrap.
That wrap or unwrap might in turn might tell you to do another operation: wrap or unwrap.
Just do what it tells you to do.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With