Reputation: 131
I am attempting to create a secure web server for a web console, and currently I am having problems. This is my code for starting an HTTPS server:
public void startServer()
{
try
{
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket ss = (SSLServerSocket) ssf.createServerSocket(8080);
for (String s : ss.getEnabledCipherSuites())
{
logger.info(s);
}
while (true)
{
Socket s = ss.accept();
OutputStream out = s.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while (((line = in.readLine()) != null) && (!("".equals(line))))
{
System.out.println(line);
}
StringBuffer buffer = new StringBuffer();
buffer.append("<HTML><HEAD><TITLE>HTTPS Server</TITLE></HEAD>\n");
buffer.append("<BODY>\n<H1>Success!</H1></BODY></HTML>\n");
String string = buffer.toString();
byte[] data = string.getBytes();
out.write("HTTP/1.0 200 OK\n".getBytes());
out.write(new String("Content-Length: " + data.length + "\n").getBytes());
out.write("Content-Type: text/html\n\n".getBytes());
out.write(data);
out.flush();
out.close();
in.close();
s.close();
}
}
catch (Throwable thrown)
{
logger.error(thrown);
}
}
This prints out the following results as enabled cipher suites:
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
And, when I try to connect to https://localhost:8080/
from my web browser (Chrome), I get the following exception:
javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ServerHandshaker.chooseCipherSuite(Unknown Source)
at sun.security.ssl.ServerHandshaker.clientHello(Unknown Source)
at sun.security.ssl.ServerHandshaker.processMessage(Unknown Source)
at sun.security.ssl.Handshaker.processLoop(Unknown Source)
at sun.security.ssl.Handshaker.process_record(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
at sun.security.ssl.AppInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.InputStreamReader.read(Unknown Source)
at java.io.BufferedReader.fill(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at net.jibini.inventory.http.InventoryHttp.startServer(InventoryHttp.java:54)
at net.jibini.inventory.http.InventoryHttp.start(InventoryHttp.java:33)
at net.jibini.inventory.server.InventoryServer$1.run(InventoryServer.java:47)
at java.lang.Thread.run(Unknown Source)
Upvotes: 3
Views: 6338
Reputation: 216
As others have mentioned using the -Djavax.net.debug=ssl will allow you to diagnose what is actually happening at a network level. The problem in this case is likely that you don't have a keystore specified so you are going to end up with no certificate/key so you won't be able to negotiate a cipher suite (e.g. TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA would require a DSA key).
So generate a key:
keytool -genkey -keystore mySrvKeystore -keyalg RSA
Then this needs to be passed either as a system property (-Djavax.net.ssl.keyStore ...) or provided using set property calls:
System.setProperty("javax.net.ssl.keyStore","mySrvKeystore");
System.setProperty("javax.net.ssl.keyStorePassword","1234567");
You can see what cipher suites are being sent by the client in the -Djavax.net.debug=all as supported but your server won't support any of them without a key/cert and will return the rather cryptic message about no cipher suites being shared.
UPDATE
As a more complete example refer to the following:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
public class TestSSL
{
public static void main( String args[] )
{
try
{
System.setProperty("javax.net.ssl.keyStore","mySrvKeystore");
System.setProperty("javax.net.ssl.keyStorePassword","1234567");
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket ss = (SSLServerSocket) ssf.createServerSocket(9001);
ss.setEnabledProtocols( new String[]{"TLSv1","TLSv1.1","TLSv1.2"} );
for (String s : ss.getEnabledCipherSuites())
{
System.out.println( s );
}
while (true)
{
Socket s = ss.accept();
OutputStream out = s.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while (((line = in.readLine()) != null) && (!("".equals(line))))
{
System.out.println(line);
}
if( line != null )
{
StringBuffer buffer = new StringBuffer();
buffer.append("<HTML><HEAD><TITLE>HTTPS Server</TITLE></HEAD>\n");
buffer.append("<BODY>\n<H1>Success!</H1></BODY></HTML>\n");
String string = buffer.toString();
byte[] data = string.getBytes();
out.write("HTTP/1.0 200 OK\n".getBytes());
out.write(new String("Content-Length: " + data.length + "\n").getBytes());
out.write("Content-Type: text/html\n\n".getBytes());
out.write(data);
out.flush();
}
out.close();
in.close();
s.close();
}
}
catch (Throwable thrown)
{
thrown.printStackTrace();
}
}
}
Note the following key differences from your previously posted example:
Upvotes: 6