写点什么

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

作者:JackJiang
  • 2023-02-24
    江苏
  • 本文字数:6101 字

    阅读完需:约 20 分钟

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

1、引言

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

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

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

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

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


学习交流:

- 移动端 IM 开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源 IM 框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此


(本文已同步发布于:http://www.52im.net/thread-4142-1-1.html

2、知识准备

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

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

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

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

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

3、什么是 Netty

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

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

4、什么是 OpenSSL

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 , 下载次数: 1 , 售价: 1 金币)

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

openssl.cnf 文件附件下载:

 openssl_conf(52im.net).rar (4.63 KB , 下载次数: 1 , 售价: 1 金币)

openssl.cnf 文件解压缩后:

openssl.cnf 文件配置使用:

以下是 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-key.pem 1024

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

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

在 CMD 控制台下执行如下指令:

openssl pkcs8 -innetty/netty-key2.pem -topk8 -out netty/netty-key2.pk8

提示 1:如不转 pk8 格式,则 Netty 的代码中使用时会报以下错误:

提示 2:如代码中不为 key 加入密码,则 Netty 的代码中使用时会报以下错误:

提示 3:Netty 的代码中使用时要加入上方生成 Key 证书时的密码即可:

6.4 第三步:创建证书请求

在 CMD 控制台下执行如下指令:

openssl req -new -out netty/netty-req2.csr -key netty/netty-key2.pem

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

6.5 第四步:生成公钥证书

在 CMD 控制台下执行如下指令:

openssl x509 -req -inca/ca-req2.csr -out netty/netty-cert2.crt -signkey netty/netty-key2.pem -days 3650

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

6.6 最终成果


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

7、实战代码

7.1 概述

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

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

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

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

我们可以将证书放到这个位置:

使用证书的示例代码片段:(完整代码详见 ServerLauncherImpl.java

/**

 * 创建 SslContext 对象,用于开启 SSL/TLS 加密传输。

 *

 * @return 如果成功创建则返回 SslContext 对象,否则返回 null

 */

privatestaticSslContext 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();                 

         returnsslCtx;

    } catch(Exception e) {

        logger.warn("createSslContext()时出错了,原因:"+e.getMessage(), e);

    }

    returnnull;

}

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

 */

publicSslContext 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);

        }


        returnsslContext;

}

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]) {

        [selfwhenDidConnect:socket];

    }

    // 如果已开启 SSL 加密传输,则需要在回调中调用 startTLS 方法,以便实现跟服务端的 SSL 握手过程,

    // 如果 ssl 握手成功,则会通过 socketDidSecure: 回调通知开发者

    else{

        // 配置 SSL/TLS 设置信息

        NSMutableDictionary*settings = [NSMutableDictionarydictionaryWithCapacity:3];

        // 允许自签名证书手动验证

        [settings setObject:@YESforKey:GCDAsyncSocketManuallyEvaluateTrust];

        // 经测试,本项不设置并不影响 SSL 的启用

//      [settings setObject:@"此处填服务器 IP 地址" forKey:GCDAsyncSocketSSLPeerName];

        // 如果不是自签名证书,而是权威证书颁发机构注册申请的证书,这个 settings 字典可不传(将使用 GCDAsyncSocket 的默认配置)

        [socket startTLS:settings];

    }

}


/**

 * 当 SSL 握手成功后(也就是上方调用 startSSL:方法后),将调用此方法。

 */

- (void)socketDidSecure:(MBGCDAsyncSocket *)socket

{

    [selfwhenDidConnect: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(^)(BOOLshouldTrustPeer))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

 */

publicSslContext 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);

        }


        returnsslContext;

}

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”:

8、参考资料

[1] MobileIMSDK开源工程源码

[2] 史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战

[3] 基于Netty,从零开发IM

[4] 基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等

[5] IM聊天系统安全手段之通信连接层加密技术

[6] 通俗易懂:一篇掌握即时通讯的消息传输安全原理

[7] 探讨组合加密算法在IM中的应用

[8] openssl安装教程(windows7系统,超详细)

[9] WebSocket从入门到精通,半小时就够!


(本文已同步发布于:http://www.52im.net/thread-4142-1-1.html

用户头像

JackJiang

关注

还未添加个人签名 2019-08-26 加入

开源IM框架MobileIMSDK、BeautyEye的作者。

评论

发布
暂无评论
手把手教你为基于Netty的IM生成自签名SSL/TLS证书_JackJiang_InfoQ写作社区