zoukankan      html  css  js  c++  java
  • Netty SSL双向验证

    一· 快速命令

    1.生成ca证书

    openssl req -new -x509 -keyout ca.key -out ca.crt -days 36500
    在本目录得到 ca.key 和 ca.crt 文件

    2.生成服务端和客户端私钥

    openssl genrsa -des3 -out server.key 1024
    openssl genrsa -des3 -out client.key 1024

    3.根据 key 生成 csr 文件

    openssl req -new -key server.key -out server.csr
    openssl req -new -key client.key -out client.csr

    4.根据 ca 证书 server.csr 和 client.csr 生成 x509 证书

    openssl x509 -req -days 36500 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
    openssl x509 -req -days 36500 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt

    5.将 key 文件进行 PKCS#8 编码

    openssl pkcs8 -topk8 -in server.key -out pkcs8_server.key -nocrypt
    openssl pkcs8 -topk8 -in client.key -out pkcs8_client.key -nocrypt

    最后得到有用的文件分别为

    • 服务器端: ca.crt、server.crt、pkcs8_server.key
    • 客户端端: ca.crt、client.crt、pkcs8_client.key

    6. 查看命令

    openssl 查看证书细节
    打印证书的过期时间
    openssl x509 -in signed.crt -noout -dates
    打印出证书的内容:
    openssl x509 -in cert.pem -noout -text
    打印出证书的系列号
    openssl x509 -in cert.pem -noout -serial
    打印出证书的拥有者名字
    openssl x509 -in cert.pem -noout -subject
    以RFC2253规定的格式打印出证书的拥有者名字
    openssl x509 -in cert.pem -noout -subject -nameopt RFC2253
    在支持UTF8的终端一行过打印出证书的拥有者名字
    openssl x509 -in cert.pem -noout -subject -nameopt oneline -nameopt -escmsb
    打印出证书的MD5特征参数
    openssl x509 -in cert.pem -noout -fingerprint
    打印出证书的SHA特征参数
    openssl x509 -sha1 -in cert.pem -noout -fingerprint
    把PEM格式的证书转化成DER格式
    openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER
    把一个证书转化成CSR
    openssl x509 -x509toreq -in cert.pem -out req.pem -signkey key.pem
    给一个CSR进行处理,颁发字签名证书,增加CA扩展项
    openssl x509 -req -in careq.pem -extfile openssl.cnf -extensions v3_ca -signkey key.pem -out cacert.pem
    给一个CSR签名,增加用户证书扩展项
    openssl x509 -req -in req.pem -extfile openssl.cnf -extensions v3_usr -CA cacert.pem -CAkey key.pem -CAcreateserial
    查看csr文件细节:
    openssl req -in my.csr -noout -text
    

    二· 部分参考代码

    服务端的代码:


    @Slf4j public class HidsSslContextBuilder { private final static String serverCrt = "/static/keys/server.crt"; private final static String serverKey = "/static/keys/pkcs8_server.key"; private final static String caCrt = "/static/keys/ca.crt"; private final static String keyPassword = ""; public static SslContext build(ClientAuth clientAuth) { InputStream certInput = null; InputStream priKeyInput = null; InputStream caInput = null; try { certInput = HidsSslContextBuilder.class.getResourceAsStream(serverCrt); priKeyInput = HidsSslContextBuilder.class.getResourceAsStream(serverKey); caInput = HidsSslContextBuilder.class.getResourceAsStream(caCrt); return SslContextBuilder.forServer(certInput, priKeyInput) .clientAuth(clientAuth) .trustManager(caInput).build(); } catch (Throwable e) { log.error("HidsSslContextBuilder", e); } finally { IOUtils.closeQuietly(certInput); IOUtils.closeQuietly(priKeyInput); IOUtils.closeQuietly(caInput); } return null; } public static SslContext buildSelfSignedCer() { try { SelfSignedCertificate ssc = new SelfSignedCertificate(); return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()) .build(); } catch (Throwable e) { log.error("buildSelfSignedCer", e); } return null; } } ... @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); // INBOUND: from head to tail // OUTBOUND: from tail to head // ssl if (sslContext != null) { pipeline.addLast(sslContext.newHandler(socketChannel.alloc())); } pipeline.addLast("TLVDecoder", new TLVDecoder()); pipeline.addLast("TLVEncoder", new TLVEncoder()); pipeline.addLast("Decompressor", new Decompressor()); pipeline.addLast("Compressor", new Compressor()); pipeline.addLast("tlvChannelHandler", tlvChannelHandler); // 增加channel对应的数据 prepareChannelContext(socketChannel); }

    客户端的测试代码

    public class ClientChannelTest {
    
        @Test
        public void testClient() throws Throwable {
            String host = "0.0.0.0";
            int port = 8888;
    
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                final SslContext sslCtx = SslContextBuilder.forClient()
                        // 双向验证
    //                    .keyManager(this.getClass().getResourceAsStream("/keys/client.crt"),
    //                            this.getClass().getResourceAsStream("/keys/pkcs8_client.key"))
    
                        // CA证书,验证对方证书
                        .trustManager(this.getClass().getResourceAsStream("/keys/ca.crt"))
    
                        // 不验证SERVER
                        // .trustManager(InsecureTrustManagerFactory.INSTANCE)
                        .build();
    
                Bootstrap b = new Bootstrap(); // (1)
                b.group(workerGroup); // (2)
                b.channel(NioSocketChannel.class); // (3)
                b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
                b.handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()));
                        ch.pipeline().addLast(new TLVDecoder());
                        ch.pipeline().addLast(new TLVEncoder());
                        ch.pipeline().addLast("Decompressor", new Decompressor());
                        ch.pipeline().addLast("Compressor", new Compressor());
                        ch.pipeline().addLast(new ClientHandler());
                    }
                });
    
                // Start the client.
                ChannelFuture f = b.connect(host, port).sync(); // (5)
    
                // Wait until the connection is closed.
                f.channel().closeFuture().sync();
            } finally {
                workerGroup.shutdownGracefully();
            }
    
        }
    }
    

    在Channel上绑定链接上下文信息,类似Session的功能,存储在Channel中的Attr中。

    public class ChannelUtils {
        public static ClientContext getOrCreate(Channel socketChannel) {
            AttributeKey<ClientContext> key = AttributeKey.valueOf(ClientContext.class.toString());
            Attribute<ClientContext> attr = socketChannel.attr(key);
            if (attr.get() == null) {
                attr.set(createClientContext(socketChannel));
            }
            return attr.get();
        }
    
        private static ClientContext createClientContext(Channel socketChannel) {
            return new DefaultClientContext(socketChannel.pipeline());
        }
    }
    
    

    三· 参考

  • 相关阅读:
    javaScript 与JQuery 计算器练习
    git在java项目中配置.gitignore不生效的解决办法
    mysql8主从复制配置
    ES6的Promise实例
    常用正则表达式
    Redis在Windows环境下后台启动
    Redis数据操作命令 二
    Redis数据操作命令
    List集合学习笔记
    MyBatis学习链接
  • 原文地址:https://www.cnblogs.com/exmyth/p/14808872.html
Copyright © 2011-2022 走看看