gpt4 book ai didi

java - 网络 SSL : Chat Client example with custom keystore is unable to accept multiple connections

转载 作者:太空宇宙 更新时间:2023-11-03 14:57:56 26 4
gpt4 key购买 nike

我一直在玩 Netty(版本 4.0.24)的 SecureChatServer 示例,并且插入了我自己的 keystore (基于在以下两篇文章 post 1post2 中找到的答案。请参阅下面的代码片段.

我看到的问题是,当我运行一个客户端实例时,一切都按预期工作,但是当我尝试启动更多客户端实例时,我在服务器和客户端上都遇到了异常(异常文本见下文)和那时服务器停止响应。

我希望这里有人可以阐明我做错了什么。

这是处理加载我的 keyStore 和创建客户端/服务器使用的 SSLContext 实例的类(出于明显的原因,我省略了我的 keystore 值):

package com.test.securechat;

import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import com.test.util.Key;
import com.pragmafs.util.encryption.Base64Coder;

/**
* Creates SSLContext instances from a static (String representation) keystore
* See http://maxrohde.com/2013/09/07/setting-up-ssl-with-netty/
*/
public class SslContextFactory {
private static final String PROTOCOL = "TLS";
private static final SSLContext SERVER_CONTEXT;
private static final SSLContext CLIENT_CONTEXT;
private static final TrustManagerFactory trustManagerFactory;

static {

SSLContext serverContext = null;
SSLContext clientContext = null;

try {

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new ByteArrayInputStream(Base64Coder.decode(Key.SSL.getKey())), Base64Coder.decodeString(Key.SSL.getPwd()).toCharArray());

// Set up key manager factory to use our key store
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, Base64Coder.decodeString(Key.SSL.getPwd()).toCharArray());

// truststore
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new ByteArrayInputStream(Base64Coder.decode(Key.SSL.getKey())), Base64Coder.decodeString(Key.SSL.getPwd()).toCharArray());

// set up trust manager factory to use our trust store
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(ts);

// Initialize the SSLContext to work with our key managers.
serverContext = SSLContext.getInstance(PROTOCOL);
serverContext.init(kmf.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

clientContext = SSLContext.getInstance(PROTOCOL);
clientContext.init(null, trustManagerFactory.getTrustManagers(), null);
} catch (Exception e) {
throw new Error("Failed to initialize the SSLContext", e);
}

SERVER_CONTEXT = serverContext;
CLIENT_CONTEXT = clientContext;
}


public static SSLContext getServerContext() {
return SERVER_CONTEXT;
}

public static SSLContext getClientContext() {
return CLIENT_CONTEXT;
}

public static TrustManagerFactory getTrustManagerFactory() {
return trustManagerFactory;
}

private SslContextFactory() {
// Unused
}
}

这是服务器代码:

package com.test.securechat;

import java.net.InetAddress;
import javax.net.ssl.SSLEngine;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.GlobalEventExecutor;


/**
* Simple SSL chat server
*/
public final class SecureChatServer {

static final int PORT = Integer.parseInt(System.getProperty("port", "8992"));

public static void main(String[] args) throws Exception {

SSLEngine sslEngine = SslContextFactory.getServerContext().createSSLEngine();
sslEngine.setUseClientMode(false);

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new SecureChatServerInitializer(sslEngine));

b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

private static class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> {

private final SSLEngine sslCtx;

public SecureChatServerInitializer(SSLEngine sslCtx) {
this.sslCtx = sslCtx;
}

@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();

pipeline.addLast(new SslHandler(sslCtx));
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());

// and then business logic.
pipeline.addLast(new SecureChatServerHandler());
}
}

private static class SecureChatServerHandler extends SimpleChannelInboundHandler<String> {

static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

@Override
public void channelActive(final ChannelHandlerContext ctx) {
// Once session is secured, send a greeting and register the channel to the global channel
// list so the channel received the messages from others.
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(Future<Channel> future) throws Exception {
ctx.writeAndFlush(
"Welcome to " + InetAddress.getLocalHost().getHostName() + " secure chat service!\n");
ctx.writeAndFlush(
"Your session is protected by " +
ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
" cipher suite.\n");

channels.add(ctx.channel());
}
});
}

@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// Send the received message to all channels but the current one.
for (Channel c : channels) {
if (c != ctx.channel()) {
c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n');
} else {
c.writeAndFlush("[you] " + msg + '\n');
}
}

// Close the connection if the client has sent 'bye'.
if ("bye".equals(msg.toLowerCase())) {
ctx.close();
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}

客户端代码:

package com.test.securechat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.net.ssl.SSLEngine;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslHandler;

/**
* Simple SSL chat client
*/
public final class SecureChatClient {

static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8992"));

public static void main(String[] args) throws Exception {

SSLEngine engine = SslContextFactory.getClientContext().createSSLEngine();
engine.setUseClientMode(true);

EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new SecureChatClientInitializer(engine));

// Start the connection attempt.
Channel ch = b.connect(HOST, PORT).sync().channel();

// Read commands from the stdin.
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (; ; ) {
String line = in.readLine();
if (line == null) {
break;
}

// Sends the received line to the server.
lastWriteFuture = ch.writeAndFlush(line + "\r\n");

// If user typed the 'bye' command, wait until the server closes
// the connection.
if ("bye".equals(line.toLowerCase())) {
ch.closeFuture().sync();
break;
}
}

// Wait until all messages are flushed before closing the channel.
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
} finally {
// The connection is closed automatically on shutdown.
group.shutdownGracefully();
}
}

private static class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> {

private final SSLEngine sslCtx;

public SecureChatClientInitializer(SSLEngine sslCtx) {
this.sslCtx = sslCtx;
}

@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();

// Add SSL handler first to encrypt and decrypt everything.
// In this example, we use a bogus certificate in the server side
// and accept any invalid certificates in the client side.
// You will need something more complicated to identify both
// and server in the real world.
//pipeline.addLast(sslCtx.newHandler(ch.alloc(), SecureChatClient.HOST, SecureChatClient.PORT));
pipeline.addLast(new SslHandler(sslCtx));

// 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());
//pipeline.addLast(new LoggingHandler(LogLevel.INFO));


// and then business logic.
pipeline.addLast(new SecureChatClientHandler());
}
}

private static class SecureChatClientHandler extends SimpleChannelInboundHandler<String> {

@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.err.println(msg);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}

服务器异常:

INFO: [id: 0x5eb2ac7a, /0:0:0:0:0:0:0:0:8992] RECEIVED: [id: 0x7d7a7dca, /127.0.0.1:55932 => /127.0.0.1:8992]
io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: ciphertext sanity check failed
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:278)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:147)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:130)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.net.ssl.SSLHandshakeException: ciphertext sanity check failed
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1683)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:959)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:884)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:995)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:921)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:867)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:247)
... 12 more
Caused by: javax.crypto.BadPaddingException: ciphertext sanity check failed
at sun.security.ssl.InputRecord.decrypt(InputRecord.java:147)
at sun.security.ssl.EngineInputRecord.decrypt(EngineInputRecord.java:192)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:953)
... 19 more

客户端异常:

io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: Received fatal alert: <UNKNOWN ALERT: 170>
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:278)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:147)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:130)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.net.ssl.SSLException: Received fatal alert: <UNKNOWN ALERT: 170>
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1619)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1587)
at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1756)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1060)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:884)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:995)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:921)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:867)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:247)
... 12 more

最佳答案

你能解释一下为什么你用信任工厂初始化你的服务器 SSLContext 但不向客户端 SSLContext 初始化方法提供任何 key 工厂吗?

您是否正在尝试进行相互身份验证?如果是这样,您还需要调用 SSLEngine.setNeedClientAuth(true)

你见过SslContext.java吗? ?这可能能够简化您的初始化过程并减少您构建自己的 SSLContext 对象的需要。如果您的证书是 X509 并且您的 key 是 PKCS#8 格式,那么您不必担心信任管理器或 key 管理器,因为 SslContext可以读取这些并为您初始化所有内容(通过 newClientContextnewServerContext)。如果需要,这些接口(interface)还支持相互身份验证。

关于java - 网络 SSL : Chat Client example with custom keystore is unable to accept multiple connections,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26746332/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com