默认
打赏 发表评论 13
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
手把手教你为基于Netty的IM生成自签名SSL/TLS证书
阅读(50826) | 评论(13 收藏2 淘帖1 1
微信扫一扫关注!

1、引言


对于IM聊天应用来说,为了提升安全性,对聊天消息加密是常规操作。

众所周之,Netty是高性能的Java NIO网络通信框架,因而用Netty来写IM是再正常不过了。网上关于为Netty生成、以及使用SSL/TLS证书的文章有很多,但由于各种原因,生成的证书要么是Netty中无法读取和使用,要么是代码不全或不具体导致根本配不通SSL/TLS加密。

正好这段时间专门为 MobileIMSDK 生成了一套测试证书,顺手把这个过程记录了下来,分享给大家。

本文要分享的是如何使用OpenSSL生成在基于Netty的IM中真正可用的SSL/TLS证书,内容包括:证书的创建、创建过程中的注意点,以及在Server端、Android端、iOS端、Java桌面端、H5端使用证书的代码范例。

手把手教你为基于Netty的IM生成自签名SSL/TLS证书_cover-opti.png

注:对于那些付费购买了第3方权威CA机构签发的证书,他们都有相应的使用文档,这就没什么好说的。本文里的证书指的是不需要花钱的自签名证书

本文已同步发布于即时通讯网公众号,链接是:https://mp.weixin.qq.com/s/1N2WInPTSgsqpYvD0TQaaA

2、知识准备


► 如果你对IM系统毫无概念,建议先阅读《零基础IM开发入门(一):什么是IM系统?》系列文章,通俗易懂,适合小白。

► 如果你想系统学习IM开发相关的理论知识,比如网格编程、IM架构设计等,建议先阅读《新手入门一篇就够:从零开发移动端IM》。

如果你不了解Netty是什么,建议阅读以下几篇Netty的基础入门好文章:


► 如果你已掌握IM理论知识,同时也对Netty基本掌握,正准备动手实战,则可以阅读《基于Netty,从零开发IM》和《跟着源码学IM》这个系列文章,有各种入门级实战代码,图文并茂,适合学习。

► 如果你对IM、Netty已基本上手,但对IM安全方面的技术概念有点理不清,建议必读《基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等》。

3、什么是Netty


手把手教你为基于Netty的IM生成自签名SSL/TLS证书_1.png

Netty是一个Java NIO技术的开源异步事件驱动的网络编程框架,用于快速开发可维护的高性能协议服务器和客户端。往通俗了讲,可以将Netty理解为:一个将Java NIO进行了大量封装,并大大降低Java NIO使用难度和上手门槛的超牛逼框架。(引用自《史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战

PS:限于篇幅,对于Netty方面的入门知识就不再赘述,如有必要,请仔细跟着本文第二节“2、知识准备”里有关Netty的文章进行阅读。

4、什么是OpenSSL


手把手教你为基于Netty的IM生成自签名SSL/TLS证书_2.png

OpenSSL是一个开放源代码的软件库,应用程序可以使用这个包来进行安全通信,它包括代码、脚本、配置和过程的集合。其主要库是以 C 语言所写成,实现了基本的加密功能,实现了 SSL 与 TLS 协议。OpenSSL整个软件包大概可以分成三个主要功能部分:SSL协议库、应用程序、密码算法库。

PS:OpenSSL的介绍就点到为止,如有兴趣,可仔细阅读《基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等》。

5、下载和安装OpenSSL


1)方法一:可以从OpenSSL的Github仓库下载源码自行编译(源码下载地址),对于一般使用者来说,自已编译着实有点麻烦,不推荐这么玩。

2)方法一:也可以从这个网站下载第3方编译好的OpenSSL安装程序(安装程序下载地址),这样上手简单快捷。具体可以参考《openssl安装教程(windows7系统,超详细)》这篇文章。

3)方法一:也可以直接用下面附件里的安装程序(这是我一直用的版本,版本较老,有兴趣可直接下载使用):
Openssl-windows-0.9.8k(52im.net).rar (874.97 KB , 下载次数: 125 , 售价: 1 金币)

4)解决 “openssl.cnf找不到” 的问题:如果你安装好OpenSSL后,使用时报“openssl.cnf找不到”或“计算机缺少openssl.cnf”等之类错误提示,可以下载下面这个 openssl.cnf文件。

openssl.cnf 文件附件下载:
openssl_conf(52im.net).rar (4.63 KB , 下载次数: 50 , 售价: 1 金币)

openssl.cnf 文件解压缩后:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_3.png

openssl.cnf文件配置使用:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_4.png

以下是 openssl.cnf 文件的配置使用命令:(以我的安装目录为例
C:\Openssl-windows-0.9.8k-out32dll>set OPENSSL_CONF=c:/WINDOWS/system32/openssl.cnf

准备就绪,接下来我们就可以开始生成SSL/TLS证书了!

6、生成Netty可用的SSL/TLS证书


6.1概述


经过实践,生成Netty可用的SSL/TLS证书需要4步:

  • 1)创建私钥证书;
  • 2)将私钥格式转成pk8;
  • 3)创建证书请求;
  • 4)生成公钥证书。

接下来,跟着本节内容,一步步使用OpenSSL生成一个真正能在Netty中能使用的自签名证书。

6.2第一步:创建私钥证书


在CMD控制台下执行如下指令:记得手动创建 netty 目录
openssl genrsa -des3 -out netty/netty-key2.pem 1024
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_5.png

提示:以上指令中,如无“-des3”参数,则Netty的代码中使用时将报“File does not contain valid private key”等错误(如下图所示)。

手把手教你为基于Netty的IM生成自签名SSL/TLS证书_6.png

6.3第二步:将私钥格式转成pk8


在CMD控制台下执行如下指令:
openssl pkcs8 -in netty/netty-key2.pem -topk8 -out netty/netty-key2.pk8
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_7.png

提示1:如不转pk8格式,则Netty的代码中使用时会报以下错误:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_8.png

提示2:如代码中不为key加入密码,则Netty的代码中使用时会报以下错误:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_9.png

提示3:Netty的代码中使用时要加入上方生成Key证书时的密码即可:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_10.png

6.4第三步:创建证书请求


在CMD控制台下执行如下指令:
openssl req -new -out netty/netty-req2.csr -key netty/netty-key2.pem
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_11.png

提示:经上指令中,Common Name指明的是证书绑定的域名,你可以用域名或ip,本次生成用了子域名。

6.5第四步:生成公钥证书


在CMD控制台下执行如下指令:
openssl x509 -req -in ca/netty-req2.csr -out netty/netty-cert2.crt -signkey netty/netty-key2.pem -days 3650
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_12.png

提示:out 参数生成的是.crt,而在前面的是.pem,这只是扩展名区别,内容都一样。

6.6最终成果


手把手教你为基于Netty的IM生成自签名SSL/TLS证书_13.png

手把手教你为基于Netty的IM生成自签名SSL/TLS证书_14.png

至此,我们已经为Netty创建好了证书,接下来的章节,就是分享如何读取和使用这些证书的。

7、实战代码


7.1概述


本节将为你演示如何在基于Netty的IM中使用上节中生成的证书。

为了让示例代码更具实战意义,本节的示例代码将引用的是开源IM框架MobileIMSDK 的源码,如果有兴趣深入学习,可以从下面的开源仓库中下载到MobileIMSDK的完整源码。


7.2基于Netty的IM服务端如何开启SSL/TLS


首先将上节中生成的证书,放置到你的IM服务端磁盘目录下。以下截图和示例代码以MobileIMSDK的开源代码为例。

我们可以将证书放到这个位置:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_15.png

使用证书的示例代码片段:完整代码详见 ServerLauncherImpl.java
/**
 * 创建SslContext对象,用于开启SSL/TLS加密传输。
 * 
 * @return 如果成功创建则返回SslContext对象,否则返回null
 */
private static SslContext createSslContext() {                
    try {
         // 证书文件
         InputStream certChainFile = ServerLauncherImpl.class.getResourceAsStream("certs/netty-cert2.crt");
         // 私钥文件(注意:Netty只支持.pk8格式)
         InputStream keyFile = ServerLauncherImpl.class.getResourceAsStream("certs/netty-key2.pk8");
         // 私钥密码
         String keyPassword = "123456";
         // 生成SslContext对象(为了方便理解,此处使用的是单向认证)
         SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();                  
         return sslCtx;
    } catch (Exception e) {
        logger.warn("createSslContext()时出错了,原因:"+e.getMessage(), e);
    }
    return null;
}

PS:如果你想自已动手完整运行一下,可以阅读《MobileIMSDK的Demo使用帮助:Server端》。

接下来的内容,我们将实现客户端连接到使用SSL/TLS证书的Netty IM服务端。

7.3Android端如何开启SSL/TLS


因为服务端已经开启了SSL/TLS加密,我们在开发IM的客户端时,该如何启用SSL/TLS呢(否则你未开启SSL/TLS的客户端肯定是连不上你的服务端的)?

这里为了方便示例,我们同样以 MobileIMSDK的Android端开源代码为例。

Android端开启SSL/TLS加密的示例代码片段:完整代码详见 IMClientManager.java
/**
 * 创建SslContext对象,用于开启SSL/TLS加密传输。
 *
 * @return 如果成功创建则返回SslContext对象,否则返回null
 */
public SslContext createSslContext() {
        SslContext sslContext = null;
        try {
                sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
                Log.d(TAG, "【IMCORE-TCP】已开启SSL/TLS加密(单向认证),且sslContext创建成功。");
        } catch (Exception e) {
                Log.w(TAG, "【IMCORE-TCP】创建sslContext时出错,原因是:" + e.getMessage(), e);
        }

        return sslContext;
}

PS:如果你想自已动手完整运行一下,可以阅读《MobileIMSDK的Demo使用帮助:Android版》。

7.4iOS端如何开启SSL/TLS


同样的,iOS端该如何开启SSL/TLS呢?

这里我们依然以 MobileIMSDK的iOS端开源代码为例(MobileIMSDK的iOS使用的是 CocoaAsyncSocket 网络库,如果你也是用的它,就可以直接参考了,因为开启了SSL/TLS的CocoaAsyncSocket代码跟未开启加密的代码用法差异较多,且这方面可以参考的资料较少)。

iOS端开启SSL/TLS加密的示例代码片段:完整代码详见 LocalSocketProvider.m
/**
 * 当socket已经完整连接并准备好读和写数据时,将调用此方法。
 */
- (void)socket:(MBGCDAsyncSocket *)socket didConnectToHost:(NSString *)host port:(uint16_t)port
{
    if([ClientCoreSDK isENABLED_DEBUG])
        NSLog(@"【IMCORE-TCP-SOCKET】成收到的了TCP的connect反馈, isConnected? %d、已开启ssl加密? %d", [socket isConnected], [ClientCoreSDK isSSL]);
    
    // 如果未开启SSL加密传输,则正常进入连接完成后的代码逻辑
    if(![ClientCoreSDK isSSL]) {
        [self whenDidConnect:socket];
    }
    // 如果已开启SSL加密传输,则需要在回调中调用startTLS方法,以便实现跟服务端的SSL握手过程,
    // 如果ssl握手成功,则会通过 socketDidSecure: 回调通知开发者
    else {
        // 配置 SSL/TLS 设置信息
        NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
        // 允许自签名证书手动验证
        [settings setObject:@YES forKey:GCDAsyncSocketManuallyEvaluateTrust];
        // 经测试,本项不设置并不影响SSL的启用
//      [settings setObject:@"此处填服务器IP地址" forKey:GCDAsyncSocketSSLPeerName];
        // 如果不是自签名证书,而是权威证书颁发机构注册申请的证书,这个settings字典可不传(将使用GCDAsyncSocket的默认配置)
        [socket startTLS:settings];
    }
}

/**
 * 当SSL握手成功后(也就是上方调用startSSL:方法后),将调用此方法。
 */
- (void)socketDidSecure:(MBGCDAsyncSocket *)socket
{
    [self whenDidConnect:socket];
}

/**
 * Allows a socket delegate to hook into the TLS handshake and manually validate the peer it's connecting to.
 */
- (void)socket:(MBGCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
{
    NSLog(@"【IMCORE-TCP-SOCKET】didReceiveTrust...");
    
    // 以下没有做更复杂的ssl证书验证逻辑,如您需要实现更强大的双向认证等逻辑,可以参考这里:
    // [url=https://github.com/FuangCao/cavan/blob/338ca8c09d6c78c5b38b95c6ffe994241afcc96e/xcode/TestSSL/TestSSL/ViewController.m]https://github.com/FuangCao/cava ... SL/ViewController.m[/url]
    if(completionHandler) {
        completionHandler(YES);
    }
}

说明:CocoaAsyncSocket中开启SSL/TLS并不像Android和Java中那么简单,它不只是几行代码的事,而是整个数据读取逻辑的变化。

PS:如果你想自已动手完整运行一下,可以阅读《MobileIMSDK的Demo使用帮助:iOS版》。

7.5Java桌面端如何开启SSL/TLS


Java桌面端开启SSL/TLS的代码跟Android端是一样。我们同样以 MobileIMSDKJava端开源代码为例。

Java桌面端开启SSL/TLS加密的示例代码片段:完整代码详见 IMClientManager.java
/**
 * 创建SslContext对象,用于开启SSL/TLS加密传输。
 * 
 * @return 如果成功创建则返回SslContext对象,否则返回null
 */
public SslContext createSslContext() {
        SslContext sslContext = null;
        try {
                sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
                Log.d(TAG, "【IMCORE-TCP】已开启SSL/TLS加密(单向认证),且sslContext创建成功。");
        } catch (Exception e) {
                Log.w(TAG, "【IMCORE-TCP】创建sslContext时出错,原因是:" + e.getMessage(), e);
        }
        
        return sslContext;
}

PS:如果你想自已动手完整运行一下,可以阅读《MobileIMSDK的Demo使用帮助:Java版》。

7.6H5端如何开启SSL/TLS


我这里说的H5端,指的是能支持标准HTML5端WebSocket协议的PC浏览器端、手机移动端内嵌的Web引擎等场景。

H5端能开启SSL/TLS有两个前提:

  • 1)第3方CA机构签发的SSL/TLS证书(这条是关键,不然浏览器因安全原因会阻止WebSocket连接的建立);
  • 2)基于Netty的IM服务端已开启SSL/TLS(见本章“7.2 基于Netty的IM服务端如何开启SSL/TLS”)。

满足以上两点后,H5端什么代码都不需改动,只需将请求url由“ws”改成“wss”:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_16.png

8、参考资料


[1] MobileIMSDK开源工程源码
[2] 史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战
[3] 基于Netty,从零开发IM
[4] 基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等
[5] IM聊天系统安全手段之通信连接层加密技术
[6] 通俗易懂:一篇掌握即时通讯的消息传输安全原理
[7] 探讨组合加密算法在IM中的应用
[8] openssl安装教程(windows7系统,超详细)
[9] WebSocket从入门到精通,半小时就够!

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:IM通讯协议专题学习(六):手把手教你如何在Android上从零使用Protobuf下一篇:不为人知的网络编程(十五):深入操作系统,一文搞懂Socket到底是什么

本帖已收录至以下技术专辑

推荐方案
评论 13
作者您好,请问ssl证书可以放nginx配置吗?
引用:921124136 发表于 2023-02-20 17:59
作者您好,请问ssl证书可以放nginx配置吗?

这要看nginx需要什么样格式的证书了,你按它要求的格式生成即可。

本篇文章里,我是针对netty要求的格式和证书属性生成的专用证书,别的服务能不能用,这个还得专门去看待。

你可以看看阿里云那边权威CA签发的收费证书也是一个道理,会针对各种不同的应用去生成专用证书格式:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_图片1.png
引用:JackJiang 发表于 2023-02-20 18:17
这要看nginx需要什么样格式的证书了,你按它要求的格式生成即可。

本篇文章里,我是针对netty要求的格 ...

好的
请问下,nginx的配置wss的ssl和netty 的ssl可以是同一个吗
引用:jeremy111 发表于 2023-04-25 20:15
请问下,nginx的配置wss的ssl和netty 的ssl可以是同一个吗

我没实践配过,但理论上可以
请问下,ca/netty-req2.csr这个文件来自于哪里?我执行到第四步骤卡在这儿了
引用:程序猿贵在坚持 发表于 2024-04-24 11:39
请问下,ca/netty-req2.csr这个文件来自于哪里?我执行到第四步骤卡在这儿了

看看这一节:“6.4 第三步:创建证书请求
您好,我已经完成了第三步:创建证书请求,但是并没有生成ca/netty-req2.csr这个文件,求大佬指教
引用:程序猿贵在坚持 发表于 2024-04-24 13:53
您好,我已经完成了第三步:创建证书请求,但是并没有生成ca/netty-req2.csr这个文件,求大佬指教

那生成了啥。。总得有个结果吧
引用:JackJiang 发表于 2024-04-24 15:20
那生成了啥。。总得有个结果吧

G:\OpenSsl\Openssl-windows-0.9.8k\out32dll>openssl req -new -out netty/netty-req2.csr -key netty/netty-key2.pem
Enter pass phrase for netty/netty-key2.pem:
Loading 'screen' into random state - done
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:cn
State or Province Name (full name) [Some-State]:tianjin
Locality Name (eg, city) []:tianjin
Organization Name (eg, company) [Internet Widgits Pty Ltd]:personal
Organizational Unit Name (eg, section) []:personal
Common Name (e.g. server FQDN or YOUR name) []:127.0.0.1
Email Address []:767508014@qq.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123456
An optional company name []:personal

G:\OpenSsl\Openssl-windows-0.9.8k\out32dll>A


啥也没生成,这是执行第三步命令(图片提交不了,就把文字复制出来了)



引用:程序猿贵在坚持 发表于 2024-04-24 16:32
G:\OpenSsl\Openssl-windows-0.9.8k\out32dll>openssl req -new -out netty/netty-req2.csr -key netty/n ...

这是我的目录情况,总感觉你的这个目录不太对,重点看看你的netty目录存不存在,不存在就手动创建:
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_WX20240424-184546@2x.png
自己下载的最新版本,按照上面的命令生成证书,项目验证报错。使用上面提供的版本openssl,使用正常
引用:beichen 发表于 2024-06-17 10:12
自己下载的最新版本,按照上面的命令生成证书,项目验证报错。使用上面提供的版本openssl,使用正常

这种通用工具尽量不要去下载很新的版本
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部