Java SSL握手exception – “无法找到有效的证书路径”

我尝试使用安全SSL(TLS)连接和双向SSL身份validation在Java上创建服务器和客户端应用程序。 单向SSL(无客户端身份validation)运行良好。 启用客户端身份validation客户端无法与exception进行握手:

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

服务器没有任何例外。 我在服务器和客户端使用Netty。 我为服务器和客户端使用自签名证书。 服务器和客户端 – 它现在是一个物理主机。 我已经使用此工具在truststore中添加了服务器证书:

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

客户代码。 主要。

 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(); } } } 

客户代码。 SClientInitializer。

 public class SClientInitializer extends ChannelInitializer { 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()); } } 

服务器代码。 主要。

 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(); } } } 

服务器代码。 初始化程序。

 public class ServerNetInitializer extends ChannelInitializer { 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()); } } 

更新1。

类JdkSslClientContext和JdkSslServerContext帮助我。

在服务器端:

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

在客户端:

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

代码示例: https : //github.com/netty/netty/blob/master/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java

更新2

在服务器端更好地使用TrustManagerFactory而不是客户端证书的File对象,因为您可能有许多客户端:

 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(); } 

试试这个:

  • 使用keytool将证书放入密钥库:

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

  • 将系统属性添加到您的代码中

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);

你没有在连接的客户端传递私钥,所以我不知道它如何建立没有它的ssl。

此外,当您使用相同的ca商店时,您确定在导入时没有覆盖certifacte。

我使用部件Update 1Update 2更新了我的第一篇文章