3
votes

After reading through numerous answers concerning these topics, I have found myself completely unable to string together the pieces of the puzzle, I hope you will excuse me for this.

I am trying to change my simple socket connection in Java to use SSL. I would like both the server and client to authenticate themselves if possible, but only server authentication would be good start.

Currently, this is the extremely simple code on the server side:

    ServerSocket serverSocket = new ServerSocket(port);
    Socket socket = serverSocket.accept();

And this is the code on the client side:

    Socket socket = null;
    while (true) {
        try {
            socket = new Socket(ipAddress, port);
            break;
        } catch (Exception e) {}
    }

This works fine, but without SSL.

I have generated SSL certificates for the server and client using OpenSSL, ending up with:

  • A certificate for the server (PEM format)
  • A certificate for the client (PEM format)
  • A private key for the server (PEM format)
  • A private key for the client (PEM format)
  • A CA file (PEM, CER and CRT format)

From this I have used OpenSSL to create PKCS12 (.p12) keystores for both the client and the server, as follows

  • server.p12, made by doing openssl pkcs12 -export -in server-cert.pem -inkey server-private-key.pem -out server.p12
  • client.p12, made by doing openssl pkcs12 -export -in client-cert.pem -inkey client-private-key.pem -out client.p12

Then, I turned these into JKS keystores by using keytool (for the server, for example, the command was keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore server.jks -deststoretype JKS), resulting in two files named server.jks and client.jks.

I then use the following code as replacement for the previous server snippet:

    char[] keyStorePassword =  "JKSPassword".toCharArray();
    FileInputStream keyStoreFile = new FileInputStream("somepath/server.jks");
    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(keyStoreFile, keyStorePassword);
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, "PKCS12Password".toCharArray());
    SSLContext sslContext = SSLContext.getDefault();
    ServerSocket serverSocket = sslContext.getServerSocketFactory().createServerSocket(port);
    Socket socket = serverSocket.accept();

And the following code as replacement for the client snippet:

    Socket socket = null;
    while (true) {
        try {
            char[] keyStorePassword =  "JKSPassword".toCharArray();
            FileInputStream keyStoreFile = new FileInputStream("somepath/client.jks");
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(keyStoreFile, keyStorePassword);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, "PKCS12Password".toCharArray());
            SSLContext sslContext = SSLContext.getDefault();
            socket = sslContext.getSocketFactory().createSocket(ipAddress, port);
            break;
        } catch (Exception e) {}
    }

I now still get javax.net.ssl.SSLHandshakeException: no cipher suites in common on the server (and javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure on the client).

What could be wrong?

1
Which Java version do you use on client and server? - Robert
Java 8u20 (of course including SDK) both on client and server (it is the same machine, and for ip I am currently using localhost). - Infima
Your server and client codes are both pointless. You initialise a KeyStore but you never use it. Have a good look at the JSSE Reference Guide. - user207421

1 Answers

1
votes

I found the missing part of the puzzle (I believe).

Instead of the line SSLContext sslContext = SSLContext.getDefault(); on the client, I put:

    BufferedInputStream serverCertificateFile = new BufferedInputStream(new FileInputStream("somepath/server-cert.der"));
    X509Certificate serverCertificate = (X509Certificate)
              CertificateFactory.getInstance("X.509").generateCertificate(serverCertificateFile);
            sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[] {
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        throw new CertificateException();
                    }
                    @Override
                    public void checkServerTrusted(X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        boolean valid = false;
                        for (X509Certificate certificate : arg0) {
                            try {
                                certificate.verify(serverCertificate.getPublicKey());
                                valid = true;
                                break;
                            } catch (SignatureException e) {}
                        }
                        if (!valid) {
                            throw new CertificateException();
                        }
                    }
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
            }, new SecureRandom());

server-cert.der is a file, created with openssl509 -outform der -in server-cert.pem -out server-cert.der.

I was not able to find good tutorials or question on this matter on StackOverflow, so this is what I created by attempting to understand the reference guide.

Essentially, I am creating a TrustManager that, for a server, trusts it when one of the provided certificates is the one that belongs to your own server.

It appears to work, please let me know if there is anything principally wrong with this approach (I unfortunately assume there is). Thanks for having read so much!