首页 > 其他分享 >netty tls单向认证通讯

netty tls单向认证通讯

时间:2023-11-16 20:22:59浏览次数:36  
标签:tls netty java ... 单向 ssl key TLS

  • 需求背景
    项目主要分为监管侧和企业侧,企业侧实时上传数据到云端,云端汇聚业务数据,上传过程需要保证传输的安全性。

  • 技术实现
    数据上传考虑到用HTTPS或者是TCP + TLS传输。其实使用HTTPS传输协议是比较简单的,但是项目硬件使用的4G无线网卡,而且需要实时检测设备运行状态,所以使用了TCP + TLS的方式,实时性更高并且减少了传输流量。然后考虑是单向认证还是双向认证,考虑为简化交互过程就采用了单向认证,在服务端生成自签证书分发给企业侧,企业侧(客户端)使用该证书发起认证即可。时间有限,本文主要介绍整体使用技术及TLS部分,其余技术细节就不展开了。

  1. 使用技术
    jdk 1.8,netty 4.1.100.Final,boringssl(openssl), openssl 1.1.1,snakeyaml,logback,msgpack 0.6.12,mybatis-plus,Wireshark。
    netty、boringssl和openssl主要实现传输层交互和证书生成
    snakeyaml做配置管理
    logback做日志输出
    msgpack 做二进制的编解码;也有考虑用Protobuf但是操作复杂,要额外配置协议格式生成代码,不能动态实现任意类的编解码工作。
    mybatis-plus做数据库的操作
    Wireshark做网络分析

  2. 首先生成自签证书
    2.1 生成pkcs8格式的证书
    openssl genrsa -out rsa_private.key 2048
    openssl pkcs8 -topk8 -nocrypt -in rsa_private.key -out private_key_pkcs8.pem
    2.2 生成公钥
    openssl rsa -in private_key_pkcs8.pem -pubout -out public_key.pem
    2.3 使用私钥生成证书
    openssl req -new -key private_key_pkcs8.pem -x509 -days 365 -out certificate.crt -subj "/C=CN/ST=SC/L=CD/O=csin/OU=test/CN=test.com/[email protected]"
    我们会得到三个文件private_key_pkcs8.pem,public_key.pem,certificate.crt。这里私钥没有加密处理,后面会说明原因

  3. ssl部分代码
    3.1 服务端

    String serverCert = "/cert/certificate.crt";
    String serverKey = "/cert/private_key_pkcs8.pem";
    List<String> ciphers = Lists.newArrayList("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA");
    SslContext sslContext = SslContextBuilder.forServer(file(serverCert), file(serverKey))
            .sslProvider(SslProvider.OPENSSL)
            .ciphers(ciphers)
            .protocols("TLSv1.2")  // 指定支持的协议版本
            .build();
    //initChannel
    ...
    ChannelPipeline pipeline = ch.pipeline();
    //ssl处理
    SSLEngine sslEngine = sslContext.newEngine(ch.alloc());
    sslEngine.setUseClientMode(false); // 设置为服务器模式
    sslEngine.setNeedClientAuth(false); // 需要客户端验证
    SslHandler sslHandler = new SslHandler(sslEngine);
    pipeline.addFirst(sslHandler);
    ...

3.2 客户端

      List<String> ciphers = Lists.newArrayList("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA");
      SslContext sslContext = SslContextBuilder.forClient()
              .trustManager(file("/cert/certificate.crt"))
              .sslProvider(SslProvider.OPENSSL)
              .ciphers(ciphers)
              .protocols("TLSv1.2")  // 指定支持的协议版本
              .build();
     //initChannel
    ...
    ChannelPipeline pipeline = socketChannel.pipeline();
    //ssl处理
    SSLEngine sslEngine = sslContext.newEngine(socketChannel.alloc());
    sslEngine.setUseClientMode(true);
    SslHandler sslHandler = new SslHandler(sslEngine, true);
    pipeline.addLast(sslHandler);
    ...
    //监听tcp连接成功后flush一下,很重要
    future.addListener((ChannelFutureListener) futureListener -> {
      if (futureListener.isSuccess()) {
          channel = futureListener.channel();
          //刷新触发tls握手
          channel.flush();
          //连接成功后,启动定时任务
          log.info("Connect to server successfully!");
      } else {
          log.error("Failed to connect to server, try connect after 10s");
          futureListener.channel().eventLoop().schedule(this::doConnect, 10, TimeUnit.SECONDS);
      }
   });
  • 踩坑
  1. TLS协议版本使用错误
    解决:使用了TLSv1.3的版本.protocols("TLSv1.2", "TLSv1.3") // 指定支持的协议版本,而项目中jdk用的1.8,只支持TLSv1.2,所以只是用TLSv1.2就可以了.protocols("TLSv1.2") // 指定支持的协议版本
  2. 采用了密钥加密后一直以下报错,可能是本人使用方式不对,有遇到过的同学望告知,谢谢。
...
java.lang.IllegalArgumentException: Input stream does not contain valid private key.
	at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:416)
	at io.netty.handler.ssl.SslContextBuilder.forServer(SslContextBuilder.java:138)
...
Caused by: java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 48)
	at sun.security.util.ObjectIdentifier.<init>(ObjectIdentifier.java:257)
	at sun.security.util.DerInputStream.getOID(DerInputStream.java:314)
	at com.sun.crypto.provider.PBES2Parameters.engineInit(PBES2Parameters.java:267)
	at java.security.AlgorithmParameters.init(AlgorithmParameters.java:293)
	at sun.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:132)
	at sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:114)
	at sun.security.x509.AlgorithmId.parse(AlgorithmId.java:372)
	at javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:95)
	at io.netty.handler.ssl.SslContext.generateKeySpec(SslContext.java:1082)
	at io.netty.handler.ssl.SslContext.getPrivateKeyFromByteBuffer(SslContext.java:1144)
	at io.netty.handler.ssl.SslContext.toPrivateKey(SslContext.java:1134)
	at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:414)
	... 3 common frames omitted
...
  1. channelActive中直接发送消息,出现以下错误。例:sendPing(ctx)
    服务端报错:io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record:
    解决:原因是在tcp连接后,tls握手还没有完成成功,发送数据没有经过SslHandler处理。正确方式应该监听握手成功发送。例子如下:
    ...
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof SslHandshakeCompletionEvent) {
            if (((SslHandshakeCompletionEvent) evt).isSuccess()) {
                // SSL握手成功
                log.info("SSL handshake completed successfully");
                handshakeSuccess(ctx);
            } else {
                // SSL握手失败
                log.info("SSL handshake failed: " + ((SslHandshakeCompletionEvent) evt).cause());
                handshakeFailed(ctx);
            }
        }
    ...
  1. 在服务端错误配置startTls为true。例:SslHandler sslHandler = new SslHandler(sslEngine, true);
    解决:应该在客户端配置startTls为true,并且sslEngine.setUseClientMode(true);
  2. 客户端TCP连接成功后,不能发起握手,异步传输时候以下报错
    handshake failed: io.netty.handler.ssl.SslHandshakeTimeoutException: handshake timed out after 10000ms
    解决:在客户端TCP连接成功后,调用channel.flush();触发tls握手,如下:
    ...
    future.addListener((ChannelFutureListener) futureListener -> {
        if (futureListener.isSuccess()) {
            channel = futureListener.channel();
            //刷新触发tls握手
            channel.flush();
            log.info("Connect to server successfully!");
        } else {
            log.error("Failed to connect to server, try connect after 10s");
            futureListener.channel().eventLoop().schedule(this::doConnect, 10, TimeUnit.SECONDS);
        }
    });
    ...
  1. 证书的格式错误,刚开始生成PKCS#1格式证书,启动就出错
    解决:netty支持PKCS#8的格式,生成PKCS#8格式的证书。
  2. Wireshark看不到tls过程
    解决:导入生成的私钥就可以了
  • 写在最后
    文中代码实现是经笔者测试的结果,为回顾项目特此记录开发过程中的问题。由于个人精力有限,如有疏漏或者发现错误,望大家提出宝贵意见。

标签:tls,netty,java,...,单向,ssl,key,TLS
From: https://www.cnblogs.com/tieger-blog/p/17837177.html

相关文章

  • Netty - 快速开始
    一、为什么使用Netty1.NIO的缺点NIO的类库和API繁杂,学习成本高,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。需要熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能写出高质量的NIO程序。臭名昭著......
  • Netty(二)文件编程
    Netty(二)文件编程1FileChannel不能够直接打开FileChannel,只能够通过FileInputStream、FIleOutPutStream和RandomAccessFile的getChannel()方法来获取FileChannelFileInputStream获得的channel只能读FIleOutPutStream获得的channel只能写RandomAccessFile是否能读写需要根据......
  • Netty(四)NIO多线程优化
    Netty(四)NIO多线程优化​ 前面的代码都只有一个选择器,没有充分利用多核CPU,因此可以分两组选择器boss:单线程配一个选择器,专门处理accept事件,不负责数据的读写worker:创建CPU核心数的线程,每个线程配一个选择器,轮流处理read事件1多线程问题分析关键是这一部分的代码,需要保......
  • Netty(三)网络编程
    Netty(三)网络编程1阻塞和非阻塞堵塞:在没有数据可读的时候,包括数据复制的过程,线程必须堵塞等待,不会占用CPU但是线程相当于闲置在单线程下,两个堵塞的方法会相互影响,必须使用多线程,32位JVM一个线程320K,64位JVM一个线程1024K,为了减少线程数,需要采用线程池技术但是即便使用了线......
  • 记录C语言实现的单向链表
    利用C语言实现的单向链表接口函数。#include<stdio.h>#include<stdlib.h>#include<stdbool.h>typedefvoid*OSMutex_t;//duration:-1forever;0nowait;nmillionseconds.//return0ifsuccess.staticintOSMutex_lock(OSMutex_tmutex,intdurat......
  • TLS可信任自签名CA证书配置
    直接使用openssl制作的CA证书,由于没有加入访问机器的“受信任的根证书颁发机构”,导致在chrome等浏览器中访问自签名证书的网站时,会有“不可信任证书”提示,进而导致websocket无法成功建立。现在通过mkcert工具可以颁发自签名CA证书,并同时在加入“受信任的根证书颁发机构” ......
  • Golang实现grpc单向认证
    接着上篇文章写Golang简单使用grpcgolang1.15+版本上,用gRPC通过TLS实现数据传输加密时,会报错证书的问题:rpcerror:code=Unavailabledesc=connectionerror:desc="transport:authenticationhandshakefailed:x509:certificateisnotvalidforanynames,but......
  • 【Netty】使用Netty搭建简易Sokect客户端
    直接上代码创建客户端,连接到服务端,并发送消息:/**发送一条消息到socket服务端*/privatevoidsendOne(StringrawMessage){NioEventLoopGroupgroup=newNioEventLoopGroup();try{Bootstrapbootstrap=newBootstrap();......
  • 未能创建 SSL/TLS 安全通道
    事件背景对接ebay的时候,报错:未能创建SSL/TLS安全通道调试发现使用RestSharp并不会,HttpClient不行,猜测是RestSharp底层处理了TLS1.2的支持查阅资料"未能创建SSL/TLS安全通道"错误通常是由于TLS版本或加密协议不匹配引起的。通常情况下,你可以通过更新你的.NET版本来......
  • netty同时支持tcp和websocket
    最近接手了别人的netty框架实现的im的一个项目,基于tcp实现通信,但是领导要求做一个网页版的聊天,接入到目前的系统,由于第一次接触这种项目,百度一圈大部分都是通过websocket实现通信的方式,最后通过chatgpt发现确实可以同时支持tcp和websocket,现在把方式放上Netty是一个高性能、异步事......