使用2向SSL握手(客户端和服务器证书)设置Netty

16

我目前正在尝试使用Netty建立双向SSL握手,其中客户端和服务器都会展示并验证证书。

在SslHandler中似乎没有实现此功能。有人做过这个吗?我想应该将其放在SslHandler.handshake操作中,并委托给javax.net.ssl.SSLEngine?

有任何提示/技巧/先前的实现吗?

谢谢!


答案(stackoverflow不允许我以正常方式发布):我发现,在设置SslHandler之前将needClientAuth标志设置在SSLEngine对象上,就可以解决这个问题!

3个回答

13

以下是基于Netty项目中的HttpSnoop服务器示例的解决方案。

在设置客户端管道时,必须按以下方式设置SSL引擎:

public ChannelPipeline getPipeline() throws Exception {
    // Create a default pipeline implementation.
    ChannelPipeline pipeline = pipeline();

    // Uncomment the following line if you want HTTPS
    SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
    engine.setUseClientMode(false);
    engine.setNeedClientAuth(true);
    pipeline.addLast("ssl", new SslHandler(engine));

    pipeline.addLast("decoder", new HttpRequestDecoder());
    pipeline.addLast("logger", new RequestAuditLogger());
    // Uncomment the following line if you don't want to handle HttpChunks.
    pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
    pipeline.addLast("outputLogger", new ResponseAuditLogger());
    pipeline.addLast("encoder", new HttpResponseEncoder());
    // Remove the following line if you don't want automatic content compression.
    pipeline.addLast("deflater", new HttpContentCompressor());
    pipeline.addLast("handler", new HttpSnoopServerHandler());
    return pipeline;
}
}

如果您需要在SSLContext中设置信任库和密钥库(SecureChatSslContextFactory),则必须按以下方式进行修改:

public final class SecureChatSslContextFactory {


private static Logger logger = LoggerFactory.getLogger(SecureChatSslContextFactory.class);

private static final String PROTOCOL = "TLS";
private static final SSLContext SERVER_CONTEXT;
private static final SSLContext CLIENT_CONTEXT;

static {

    SSLContext serverContext = null;
    SSLContext clientContext = null;

        // get keystore and trustore locations and passwords
    String keyStoreLocation = System.getProperty("javax.net.ssl.keyStore");
    String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
    String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore");
    String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
    try {

        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(KeyStoreStreamManager.asInputStream(keyStoreLocation),
                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(KeyStoreStreamManager.asInputStream(trustStoreLocation),
                trustStorePassword.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 {
        clientContext = SSLContext.getInstance(PROTOCOL);
        clientContext.init(null, SecureChatTrustManagerFactory.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;
}

private SecureChatSslContextFactory() {
    // Unused
}
}

任何javax.net.ssl.keyStoreType属性支持的证书都是可接受的。https://docs.oracle.com/cd/B19306_01/java.102/b14355/sslthin.htm#BABHFBJD - MeowCode

9

不要设置SSLEngine,而是使用Netty的SslContext创建一个新的SslHandler。你可以通过以下方式传递KeyManagerFactory来创建新的SslContext

SslContext sslContext = SslContextBuilder.forServer(keyManagerFactory).build();

然后使用创建的SslContext获取ChannelPipeline的处理程序。

ChannelPipeline.addLast("ssl", sslContext.newHandler(socketChannel.alloc()));


6
现在,SslContext已经支持双向身份验证(目前仅支持JDK提供程序,但OpenSSL提供支持即将到来)。请参见newClientContextnewServerContext,它们都支持使用TrustManagerFactory和KeyManagerFactory。这些静态工厂方法还支持直接使用证书、密钥和证书链文件为您构建TrustManagerFactory和KeyManagerFactory。

请参见JdkSslEngineTest,了解如何要求客户端身份验证(适用于JDK提供程序)的示例。


OpenSSL引擎现在支持双向认证。OpenSSL引擎基本上具有与JDK的SSL引擎相同的功能。请参见SSLEngineTest,如果有缺少的功能,请提交问题 - Scott Mitchell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接