kwyjibo
kwyjibo

Reputation:

How to initiate handshakes in Java 8 using new GCM cipher suites

I am very new to Java security, so the answer to this might be obvious. I am trying to perform a simple handshake between a client and a server and I want them to handshake using either TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 or TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384. According to everywhere I read these ciphers are supported in Java 8 and I have installed JCE Unlimited Strength Jurisdiction Policy Files. So when I print out all the enabled ciphers in my Java installation, those two ciphers are present, which means they are enabled by default. But for some reason the handshake fails because client and server have no cipher suites in common. I enabled TLSv1.2 protocol as well. The client's public key has been imported into the server's trust store and the handshake succeed for other ciphers such as TLS_RSA_WITH_AES_128_CBC_SHA256 etc. I am running Java 8 v1.8.0_60. What else am I missing?

import static org.junit.Assert.assertEquals;
import java.io.IOException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

public class GCMCiphersJava8Test {

private static final String SERVER_KEY_STORE = "testkeystore.jks";
private static final String CLIENT_KEY_STORE = "testtruststore.jks";
private static final String HOST = "localhost";
private static final String PASSWORD = "Pa55word";
private static final int SSL_PORT = 8443;
private static final String[] TLS_12 = new String[]{"TLSv1.2"};
private static String serverKeyStorePath = null;
private static String clientKeyStorePath = null;
private Server server = null;

@BeforeClass
public static void setup() {
    serverKeyStorePath = GCMCiphersJava8Test.class.getResource(SERVER_KEY_STORE).getFile();
    clientKeyStorePath = GCMCiphersJava8Test.class.getResource(CLIENT_KEY_STORE).getFile();
}

@Test
public void testGCMCiphersInJava8() throws Exception{

    SSLSession session = null;
    startServer(TLS_12, null, new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, null);
    SSLSocket sslSocket = createSslSocket(TLS_12, null, new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, null);

    if (this.server.isRunning()){
        session = sslSocket.getSession();
    }

    assertEquals("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", session.getCipherSuite());
}

private void startServer(String[] includeProtocols, String[] excludeProtocols, String[] includeCiphers, String[] excludeCiphers) throws Exception{

    this.server = new Server();

    SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector();
    ssl_connector.setPort(SSL_PORT);

    SslContextFactory cf = ssl_connector.getSslContextFactory();
    cf.setKeyStorePath(serverKeyStorePath);
    cf.setKeyStorePassword(PASSWORD);
    cf.setKeyManagerPassword(PASSWORD);

    if (includeCiphers != null){
        cf.setIncludeCipherSuites(includeCiphers);
    }

    if (excludeCiphers != null){
        cf.setExcludeCipherSuites(excludeCiphers);
    }

    if (includeProtocols != null){
        cf.setIncludeProtocols(includeProtocols);
    }

    if (excludeProtocols != null){
        cf.setExcludeProtocols(excludeProtocols);
    }

    this.server.setConnectors(new Connector[]{ssl_connector});

    this.server.setHandler(new AbstractHandler() {

        @Override
        public void handle(String target,Request baseRequest,HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException
        {
            response.setContentType("text/html;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            baseRequest.setHandled(true);
            response.getWriter().println("<h1>Hello World</h1>");
        }
    });

    this.server.start();
}

@After
public void stopServer() throws Exception{
    this.server.stop();
}

private SSLSocket createSslSocket(String[] includeProtocols, String[] excludeProtocols, String[] includeCiphers, String[] excludeCiphers){

    SSLSocket sslSocket = null;

    try {

        System.setProperty("javax.net.ssl.trustStore", clientKeyStorePath);

        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        sslSocket = (SSLSocket) factory.createSocket(HOST, SSL_PORT);

        if (includeCiphers != null){
            sslSocket.setEnabledCipherSuites(includeCiphers);
        }

        if (includeProtocols != null){
            sslSocket.setEnabledProtocols(includeProtocols);
        }

        sslSocket.addHandshakeCompletedListener(e -> {
                System.out.println("Handshake succesful!");
                System.out.println("Using cipher suite: " + e.getCipherSuite());
        });

    } catch (Exception e) {
        e.printStackTrace();
    }

    return sslSocket;
}

}

Upvotes: 0

Views: 3340

Answers (2)

Z.T.
Z.T.

Reputation: 949

A TLS cipher suite has 4 parts, all of which it must specify. They are:

  1. "Key Exchange" which has 3 sane options:
    1. "RSA" which means client generates random, RSA-encrypts the random using server's public key, sends to server. Does not provide PFS, deprecated.
    2. "DHE" which means classic (Final Field) DHE. Older software limited to insecure 1024 bit groups, not recommended.
    3. "ECDHE" which is the only one that's recommended.
  2. "Authentication" which has 3 sane options:
    1. "aRSA" which means RSA signature. Before TLS 1.3 uses pkcs#1v1.5 instead of RSASSA-PSS, no longer recommended, 99.9% sites use this.
    2. "ECDSA" which means ECDSA signatures which requires server to have ECDSA certificate and key. Requires strong CSPRNG during handshake or private key is compromised! Google and Facebook and Cloudflare use this.
    3. "EdDSA" which is going into TLS 1.3 and is going to be the recommended one.
  3. "encryption" which has quite a few usable options:
    1. AES-GCM, an AEAD mode.
    2. chacha20-poly1305, also an AEAD mode. In Google and Cloudflare servers now and in TLS 1.3.
    3. AES (CBC), deprecated.
    4. 3DES CBC, deprecated.
    5. Many more. Only AEAD cipher suites are recommended, the rest are deprecated.
  4. "Hash" or "MAC" which has 3 sane options:
    1. (HMAC-)SHA1, still secure.
    2. (HMAC-)SHA256, used for paranoia.
    3. (HMAC-)SHA384, used for paranoia.
    4. But note AEAD cipher suites like GCM (and chacha20-poly1305) actually use their own MAC (GMAC for GCM) so the "MAC" of the cipher suite is only used as PRF for symmetric key generation, not as a MAC, and TLS 1.2 mandates PRF be at least SHA-256.

Neither AES-GCM nor ECDHE impose any constraints on the certificate or key.

ECDSA requires the server to have ECDSA certificate and key.

Thus, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 work with RSA certs and are what everyone recommends you deploy today if you don't have an ECDSA cert.

Upvotes: 4

kwyjibo
kwyjibo

Reputation:

I figured it out. To use GCM-based ciphers I needed to generate a keypair using Elliptic Curve option in keytool. I foolishly used RSA one I had before.

generate keypair using EC and 256 key size:

keytool -genkeypair -alias sergey -keyalg EC -keysize 256 -validity 365 -keystore testkeystore.jks -storepass <insertPasswordHere>

export the key into the certificate:

keytool -exportcert -keystore testkeystore.jks -storepass <insertPasswordHere> -file testCert.crt -alias <insertAliasHere>

import that certificate into the server's trust store:

keytool -importcert -trustcacerts -file testCert.crt -alias <sameAliasAsAbove> -keystore testtruststore.jks -storepass <insertPasswordHere>

Upvotes: 0

Related Questions