Set up Netty with 2-way SSL Handskake (client and server certificate)
Asked Answered
F

3

16

I am now trying to set up Netty with a 2 way SSL handshake, where both the client and server present and verify certificates.

This does not appear to be implemented in SslHandler. Has anyone does this? I suppose it would go in the SslHandler.handshake operation and be delegated to javax.net.ssl.SSLEngine?

Any hints/tips/pre-existing implementations?

Thanks!


ANSWER (stackoverflow won't let me post it the normal way) I found that if I set the needClientAuth flag on the SSLEngine object before setting up my SslHandler, that takes care of the problem!

Foreclose answered 5/3, 2012 at 20:59 Comment(0)
F
13

Here is the solution, based on the HttpSnoop server example from the netty project.

When setting up the client side pipeline, the ssl engine must be set as follows:

public ChannelPipeline getPipeline() throws Exception {
    // Create a default pipeline implementation.
    ChannelPipeline pipeline = pipeline();

    // Uncomment the following line if you want HTTPS
    SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
    engine.setUseClientMode(false);
    engine.setNeedClientAuth(true);
    pipeline.addLast("ssl", new SslHandler(engine));

    pipeline.addLast("decoder", new HttpRequestDecoder());
    pipeline.addLast("logger", new RequestAuditLogger());
    // Uncomment the following line if you don't want to handle HttpChunks.
    pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
    pipeline.addLast("outputLogger", new ResponseAuditLogger());
    pipeline.addLast("encoder", new HttpResponseEncoder());
    // Remove the following line if you don't want automatic content compression.
    pipeline.addLast("deflater", new HttpContentCompressor());
    pipeline.addLast("handler", new HttpSnoopServerHandler());
    return pipeline;
}
}

Then your SSLContext must be modified as follows to set up a trust store in addition to a keystore (SecureChatSslContextFactory):

public final class SecureChatSslContextFactory {


private static Logger logger = LoggerFactory.getLogger(SecureChatSslContextFactory.class);

private static final String PROTOCOL = "TLS";
private static final SSLContext SERVER_CONTEXT;
private static final SSLContext CLIENT_CONTEXT;

static {

    SSLContext serverContext = null;
    SSLContext clientContext = null;

        // get keystore and trustore locations and passwords
    String keyStoreLocation = System.getProperty("javax.net.ssl.keyStore");
    String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
    String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore");
    String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
    try {

        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(KeyStoreStreamManager.asInputStream(keyStoreLocation),
                keyStorePassword.toCharArray());

        // Set up key manager factory to use our key store
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, keyStorePassword.toCharArray());

          // truststore
        KeyStore ts = KeyStore.getInstance("JKS");
        ts.load(KeyStoreStreamManager.asInputStream(trustStoreLocation),
                trustStorePassword.toCharArray());

        // set up trust manager factory to use our trust store
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ts);

        // Initialize the SSLContext to work with our key managers.
        serverContext = SSLContext.getInstance(PROTOCOL);
        serverContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    } catch (Exception e) {
        throw new Error(
                "Failed to initialize the server-side SSLContext", e);
    }

    try {
        clientContext = SSLContext.getInstance(PROTOCOL);
        clientContext.init(null, SecureChatTrustManagerFactory.getTrustManagers(), null);
    } catch (Exception e) {
        throw new Error(
                "Failed to initialize the client-side SSLContext", e);
    }

    SERVER_CONTEXT = serverContext;
    CLIENT_CONTEXT = clientContext;
}

public static SSLContext getServerContext() {
    return SERVER_CONTEXT;
}

public static SSLContext getClientContext() {
    return CLIENT_CONTEXT;
}

private SecureChatSslContextFactory() {
    // Unused
}
}
Foreclose answered 6/3, 2012 at 18:31 Comment(1)
Any sort of certificate is acceptable that is supported by the javax.net.ssl.keyStoreType property. docs.oracle.com/cd/B19306_01/java.102/b14355/…Foreclose
T
9

Instead of setting SSLEngine use nettys SslContext to create a new SslHandler. Basically you can create new SslContext by passing KeyManagerFactory as follows

SslContext sslContext = SslContextBuilder.forServer(keyManagerFactory).build();

Then use created SslContext to get the handler for ChannelPipeline.

ChannelPipeline.addLast("ssl", sslContext.newHandler(socketChannel.alloc()));

Temper answered 6/11, 2015 at 11:41 Comment(0)
J
7

Mutual authentication is now supported by SslContext (currently just for JDK provider, but OpenSSL provide support is coming soon). See newClientContext and newServerContext which both now support taking a TrustManagerFactory and a KeyManagerFactory. These static factory methods also support directly taking certificate, key, and certificate chain files to build the TrustManagerFactory and KeyManagerFactory for you.

See the JdkSslEngineTest for an example of how to require client authentication (for the JDK provider).

Jeweller answered 10/11, 2014 at 0:54 Comment(1)
The OpenSSL engine now supports mutual authentication. The OpenSSL engine basically has feature parity with the JDK's SSL engine. See SSLEngineTest and if there are missing features please file an issue.Jeweller

© 2022 - 2024 — McMap. All rights reserved.