0

I try to make server and client apps on Java with secure SSL (TLS) connection and 2-way SSL authentication. 1-way SSL (without client authentication) works well. With enabled client authentication client can't make handshake with exception:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Server don't have any exceptions. I use Netty in server and client. I use self-signed certificates for server and client. Server and client - it's one physical host now. I'm already added server's certificate in truststore with this tool:

https://java-use-examples.googlecode.com/svn/trunk/src/com/aw/ad/util/InstallCert.java

Client code. Main.

public class SClientApp {

public static final String HOST = "127.0.0.1";
public static final int PORT = 8888;

public static void main(String[] args) throws Exception {

    System.setProperty("javax.net.ssl.trustStore", "/etc/ssl/certs/java/cacerts");
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

    // Configure SSL (TLS)
    File tls_cert = new File("tls/client1.pem");
    SslContext sslCtx = null;
    try {
        sslCtx = SslContext.newClientContext(tls_cert);
    } catch (SSLException e) {
        e.printStackTrace();
    }

    EventLoopGroup group = new NioEventLoopGroup();

    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
                .channel(NioSocketChannel.class)
                .handler(new SClientInitializer(sslCtx));

        // Start the connection attempt.
        Channel ch = b.connect(HOST, PORT).sync().channel();

        ...

    } finally {
        // The connection is closed automatically on shutdown.
        group.shutdownGracefully();
    }

}
}

Client code. SClientInitializer.

public class SClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;

public SClientInitializer(SslContext sslCtx) {
    this.sslCtx = sslCtx;
}

@Override
protected void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();

    SSLEngine ssl_engine = sslCtx.newEngine(ch.alloc(), SClientApp.HOST, SClientApp.PORT);
    ssl_engine.setUseClientMode(true);
    pipeline.addLast(new SslHandler(ssl_engine));

    // On top of the SSL handler, add the text line codec.
    pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    pipeline.addLast(new StringDecoder());
    pipeline.addLast(new StringEncoder());

    // and then business logic.
    pipeline.addLast(new SClientHandler());
}
}

Server code. Main.

public class ServerApp {

static final int PORT = Integer.valueOf(Params.get(Const.SERVER_PORT));

public static void main(String[] args) {

    System.setProperty("javax.net.ssl.trustStore", "/etc/ssl/certs/java/cacerts");
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

    // Configure SSL (TLS)
    File tls_cert = new File("tls/server.pem"); // SSL-cert
    File tls_key  = new File("tls/server.key.pkcs8"); // Private key
    SslContext sslCtx = null;
    try {
        sslCtx = SslContext.newServerContext(tls_cert, tls_key);
    } catch (SSLException e) {
        e.printStackTrace();
    }

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup(2);

    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerNetInitializer(sslCtx));

        ChannelFuture f = null;
        try {
            f = b.bind(PORT).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}
}

Server code. Initializer.

public class ServerNetInitializer extends ChannelInitializer<SocketChannel> {

private final SslContext sslCtx;

public ServerNetInitializer(SslContext sslCtx) {
    this.sslCtx = sslCtx;
}

@Override
protected void initChannel(SocketChannel ch) {

    ChannelPipeline pipeline = ch.pipeline();

    SSLEngine ssl_engine = sslCtx.newEngine(ch.alloc());
    ssl_engine.setUseClientMode(false);
    ssl_engine.setNeedClientAuth(true);
    pipeline.addLast(new SslHandler(ssl_engine));

    // On top of the SSL handler, add the text line codec.
    pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    pipeline.addLast(new StringDecoder());
    pipeline.addLast(new StringEncoder());

    // and then business logic.
    pipeline.addLast(new ServerNetHandler());
}
}

Update 1.

Classes JdkSslClientContext and JdkSslServerContext helps me.

On server side:

sslCtx = new JdkSslServerContext(client_tls_cert, null,
                server_tls_cert, server_tls_key, "", null,
                null, IdentityCipherSuiteFilter.INSTANCE, (ApplicationProtocolConfig) null, 0, 0);

On client side:

sslCtx = new JdkSslClientContext(server_tls_cert,null,client_tls_cert,client_tls_key,"", null, null,IdentityCipherSuiteFilter.INSTANCE,(ApplicationProtocolConfig) null,0,0);

Example of code here: https://github.com/netty/netty/blob/master/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java

Update 2

On server side better use TrustManagerFactory instead of File object of client certificate, because you may have many clients:

KeyStore ts = null;
    ts = KeyStore.getInstance("JKS");
    ts.load(new FileInputStream(System.getProperty("javax.net.ssl.trustStore")),
            System.getProperty("javax.net.ssl.trustStorePassword").toCharArray());

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

    SslContext sslCtx = null;
    try {
        sslCtx = new JdkSslServerContext(null, tmf,
                server_tls_cert, server_tls_key, "", null,
                null, IdentityCipherSuiteFilter.INSTANCE, (ApplicationProtocolConfig) null, 0, 0);

    } catch (SSLException e) {
        log.error("Making ssl context for server - Exception: " + e.toString());
        e.printStackTrace();
    }
Nik
  • 21
  • 1
  • 5
  • That googlecode program puts the server cert into either jssecacerts or JRE/lib/security/cacerts, which is almost (but not quite) the default locations used by Java/JSSE for its truststore. You however configure an explicit location for the truststore that is completely different. You should either put the cert into the default trustore and use that default truststore, or put the cert in some other truststore and use that other truststore. – dave_thompson_085 Feb 26 '15 at 03:12
  • That program create jssecacerts file in current directory. I'm replace existing /etc/ssl/certs/java/cacerts file with new file. Permissions identical. File $JAVA_HOME/jre/lib/security/cacerts it's link to /etc/ssl/certs/java/cacerts. – Nik Feb 26 '15 at 14:35
  • Looking again, is the client's SslContext.newClientContext() different for client-auth aka 2-way? In particular is this maybe producing a context that does not use the default truststore, where without client-auth it did use the default truststore (or maybe global default SSLContext)? Since this is clearly not the standard javax.net.ssl.SSLContext, I ass-u-me it is a custom class that does roughly the same thing, but here the details of that "roughly" matter. – dave_thompson_085 Feb 26 '15 at 21:03
  • For update 1: assuming you mean http://netty.io/4.0/api/io/netty/handler/ssl/JdkSslClientContext.html that ctor (overload) says it uses for trust only(?) arg#1 server_tls_cert which must contain certs in PEM if nonnull, so the discussion about setting default truststore properties and putting the server cert in a JKS is apparently irrelevant. Instead, what is in the server_tls_cert file? Re update 2: that ctor says it accepts PEM **chains** plural which **could** handle "many" clients, although the JKS-TMF approach makes sense if you have the client certs (only?) in a JKS. – dave_thompson_085 Mar 04 '15 at 05:43

4 Answers4

1

Since JdkSslClientContext is deprecated, use io.grpc.netty.GrpcSslContexts to create a io.netty.handler.ssl.SslContextBuilder.

Examples (no mutual auth):

Client

InputStream trustCertCollection = new FileInputStream("certs/ca.crt");
SslContextBuilder builder = GrpcSslContexts.forClient();
builder.trustManager(trustCertCollection);
SslContext sslContext = builder.build();

Server

InputStream certChain = new FileInputStream("certs/server.crt")
InputStream privateKey = new FileInputStream("certs/server.pk8");
SslClientContextBuilder sslClientContextBuilder = SslContextBuilder.forServer(certChain, privateKey);
SslContext sslContext = GrpcSslContexts.configure(sslClientContextBuilder).build();

Also see official examples: https://github.com/grpc/grpc-java/tree/2548bcd7c7afbbe4c6651ea96ba2b62aa336e276/examples/example-tls

mjduijn
  • 141
  • 1
  • 6
0

try this:

  • put your certificate in to keystore with keytool:

keytool -import -alias myAlias-file mycert.crt -keystore mykeystore.jks -storepass changeit

  • add System properties to your code

System.setProperty("javax.net.ssl.keyStore", keyStore); System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword); System.setProperty("javax.net.ssl.trustStore", trustStore); System.setProperty("javax.net.ssl.keyStorePassword", trustStorePassword);

konstantin
  • 126
  • 5
0

you dont pass the private key at the client side of the connection, so I dont know how it can establish ssl without it.

Also as you are using the same ca store are you sure you did not overwrite the certifacte upon import.

Zielu
  • 8,312
  • 4
  • 28
  • 41
0

I updated my first post with parts Update 1 and Update 2

Nik
  • 21
  • 1
  • 5