1. 程式人生 > >netty中實現雙向認證的SSL連線

netty中實現雙向認證的SSL連線

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

具體實現請參考附件原始碼。