我尝试使用安全SSL(TLS)连接和双向SSL身份验证在Java上创建服务器和客户端应用程序。单向SSL(无客户端身份验证)运行良好。启用客户端身份验证后,客户端无法与异常进行握手:
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<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());
}
}
服务器代码。主
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<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());
}
}
更新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);
更新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();
}
答案 0 :(得分:0)
试试这个:
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);
答案 1 :(得分:0)
你没有在连接的客户端传递私钥,所以我不知道它如何在没有它的情况下建立ssl。
同样,当您使用相同的ca商店时,您确定在导入时没有覆盖证书。
答案 2 :(得分:0)
我使用更新1 和更新2
更新了我的第一篇文章答案 3 :(得分:0)
自JdkSslClientContext
起已弃用,请使用io.grpc.netty.GrpcSslContexts
创建一个io.netty.handler.ssl.SslContextBuilder
。
示例(无相互认证)
客户
InputStream trustCertCollection = new FileInputStream("certs/ca.crt");
SslContextBuilder builder = GrpcSslContexts.forClient();
builder.trustManager(trustCertCollection);
SslContext sslContext = builder.build();
服务器
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();
另请参见官方示例:https://github.com/grpc/grpc-java/tree/2548bcd7c7afbbe4c6651ea96ba2b62aa336e276/examples/example-tls