netty中實現雙向認證的SSL連線
阿新 • • 發佈:2019-01-27
2. 建立SSL通訊的client和server
由於netty 5現在只有alpha版本,因此保險起見使用4.0.24.final版本的netty。
netty的SSLContext提供了newClientContext來為client建立ssl context,但檢視其原始碼未發現能支援雙向認證,即client端的ssl context只接收一個trust store,而不能指定自己的證書以供server端校驗。仿照netty example下的securechat的ssl實現但做了修改
首先建立一個能提供client和server的ssl context的工具類,分別載入server和client的key store和trust store裡面的證書
public class SslContextFactory { private static final String PROTOCOL = "TLS"; // TODO: which protocols will be adopted? private static final SSLContext SERVER_CONTEXT; private static final SSLContext CLIENT_CONTEXT; static { SSLContext serverContext = null; SSLContext clientContext = null; String keyStorePassword = "aerohive"; try { KeyStore ks = KeyStore.getInstance("JKS"); ks.load(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\server.keystore"), 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(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\servertruststore.keystore"), keyStorePassword.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 { // keystore KeyStore ks = KeyStore.getInstance("JKS"); ks.load(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\client.keystore"), 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(SslContextFactory.class.getClassLoader().getResourceAsStream("cert\\clienttruststore.keystore"), keyStorePassword.toCharArray()); // set up trust manager factory to use our trust store TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ts); clientContext = SSLContext.getInstance(PROTOCOL); clientContext.init(kmf.getKeyManagers(), tmf.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; } ... ... }
io.netty.example.securechat.SecureChatClientInitializer類的構造器接收一個io.netty.handler.ssl.SslContext型別的物件,這個SslContext的物件最終被用來建立SslHandler,而上面factory產生的是javax.net.ssl.SSLContext的物件,因此可以做改動如下
public class ClientInitializer extends ChannelInitializer { private final javax.net.ssl.SSLContext sslCtx; public ClientInitializer(javax.net.ssl.SSLContext sslCtx) { this.sslCtx = sslCtx; } @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); SSLEngine sslEngine = sslCtx.createSSLEngine(Client.HOST, Client.PORT); sslEngine.setUseClientMode(true); pipeline.addLast(new SslHandler(sslEngine)); // 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 ClientHandler()); } }
最後新增jvm引數
-Djavax.net.debug=ssl,handshake
來檢視ssl握手過程控制檯的log
具體實現請參考附件原始碼。