基于 Netty 的 IM 聊天加密技术学习:一文理清常见的加密概念、术语等
1、引言
在社区中,分享了很多篇基于 Netty 编写的 IM 聊天入门文章(比如《跟着源码学IM》系列、《基于Netty,从零开发IM》系列等),在这些文章中分享了各种 IM 通信算法原理和功能逻辑的实现。但是这样简单的 IM 聊天系统是比较容易被窃听的,如果想要在里面说点悄悄话是不太安全的。
怎么办呢?学过密码学的朋友可能就想到了一个解决办法,聊天的时候对消息加密,处理的时候再对消息进行解密。是的,道理就是这样。
但密码学本身的理论就很复杂,加上相关的知识和概念又太多太杂,对于 IM 入门者来说,想要快速理清这些概念并实现合适的加解密方案,是比较头疼的。
本文正好借此机会,以 Netty 编写的 IM 聊天加密为例,为入门者理清什么是 PKI 体系、什么是 SSL、什么是 OpenSSL、以及各类证书和它们间的关系等,并在文末附上简短的 Netty 代码实示例,希望能助你通俗易懂地快速理解这些知识和概念!
补充说明:本文为了让文章内容尽可能言简意赅、通俗易懂,尽量不深入探讨各个技术知识和概念,感兴趣的读者可以自行查阅相关资料进一步学习。
2、相关文章
3、什么是 PKI?
我们需要先了解一下公钥和私钥的加密标准体系 PKI。
3.1 基本概念
PKI 的全称是 Public Key Infrastructure,是指支持公钥管理体制的基础设施,提供鉴别、加密、完整性和不可否认性服务。
通俗讲:PKI 是集机构、系统(硬件和软件)、人员、程序、策略和协议为一体,利用公钥概念和技术来实现和提供安全服务的、普适性的安全基础设施。
在公钥密码中,发送者用公钥(加密密钥)加密,接收者用私钥(解密密钥)解密。公钥一般是公开的,不再担心窃听,这解决了对称密码中的密钥配送问题。但是接收者依然无法判断收到的公钥是否合法(有可能是中间人假冒的)。
事实上,仅靠公钥密码本身,无法防御中间人攻击。于是,需要(认证机构)对公钥进行签名,从而确认公钥没有被篡改。加了数字签名的公钥称为公钥证书,一般简称证书。
有了证书来认证,可以有效防御中间人攻击,随之带来了一系列非技术性工作。
例如:谁来发证书?如何发证书?不同机构的证书怎么互认?纸质证书作废容易,数字证书如何作废?解决这些问题,需要制定统一的规则,即 PKI 体系。
PKI 体系是通过颁发、管理公钥证书的方式为终端用户提供服务的系统,最核心的元素是证书。
围绕证书构成了 PKI 体系的要素:
1)使用 PKI 的用户;
2)颁发证书的机构(Certificate Authority,CA);
3)保存证书的仓库。
总之:PKI 是一个总称,既包括定义 PKI 的基础标准,也包括 PKI 的应用标准。
3.2 PKI 体系现状
事实上 PKI 已经有两代了。
第一代的 PKI 标准主要是由美国 RSA 公司的公钥加密标准 PKCS、国际电信联盟的 ITU-T X.509、IETF 的 X.509、WAP 和 WPKI 等标准组成。但是因为第一代 PKI 标准是基于抽象语法符号 ASN.1 进行编码的,实现起来比较复杂和困难,所以产生了第二代 PKI 标准。
第二代 PKI 标准是由微软、VeriSign 和 webMethods 三家公司在 2001 年发布的基于 XML 的密钥管理规范也叫做 XKMS。
事实上现在 CA 中心使用的最普遍的规范还是 X.509 系列和 PKCS 系列。
X.509 系列主要由 X.209、X.500 和 X.509 组成,其中 X.509 是由国际电信联盟(ITU-T)制定的数字证书标准。在 X.500 基础上进行了功能增强,X.509 是在 1988 年发布的。
X.509 证书由用户公共密钥和用户标识符组成。此外还包括版本号、证书序列号、CA 标识符、签名算法标识、签发者名称、证书有效期等信息。
而 PKCS 是美国 RSA 公司的公钥加密标准,包括了证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。它定义了一系列从 PKCS#1 到 PKCS#15 的标准。
其中最常用的是 PKCS#7、PKCS#12 和 PKCS#10。PKCS#7 是消息请求语法,常用于数字签名与加密,PKCS#12 是个人消息交换与打包语法主要用来生成公钥和私钥(题外话:iOS 程序员对 PKCS#12 不陌生,在实现APNs离线消推送时就需要导出.p12 证明,正是这个)。PKCS#10 是证书请求语法。
4、什么是 SSL?
4.1 基本概念
SSL(全称 Secure Socket Layer)安全套接层是网景公司(Netscape)率先采用的网络安全协议。它是在传输通信协议(TCP/IP)上实现的一种安全协议,采用公开密钥技术。
通俗地说:SSL 被设计成使用TCP来提供一种可靠的端到端的安全服务,它不是单个协议,而是二层协议。低层是 SSL 记录层,用于封装不同的上层协议,另一层是被封装的协议,即 SSL 握手协议,它可以让服务器和客户机在传输应用数据之前,协商加密算法和加密密钥,客户机提出自己能够支持的全部加密算法,服务器选择最适合它的算法。
SSL 特点是:它与应用层协议独立无关。上层的应用层协议(例如:HTTP、FTP、Telnet 等)能透明的建立于 SSL 协议之上。SSL 协议在应用层协议通信之前就已经完成加密算法、通信密钥的协商以及服务器认证工作。在此之后应用层协议所传送的数据都会被加密,从而保证通信的私密性。
4.2 与 TLS 的关系
SSL 是网景公司(Netscape)设计,但 IETF 将 SSL 作了标准化,即RFC2246,并将其称为 TLS(Transport Layer Security),其最新版本是 RFC5246、版本 1.2。
实际上:TLS 是 IETF 在 SSL3.0 基础上设计的,相当于 SSL 的后续版本。所以我们通常都是 SSL/TLS 放一起说。
5、什么是 OpenSSL?
5.1 基本概念
OpenSSL是一个开放源代码的软件库,应用程序可以使用这个包来进行安全通信,它包括代码、脚本、配置和过程的集合。例如:如果您正在编写一个需要复杂安全加密的软件,那么只有添加一个安全加密库才有意义,这样您就不必自己编写一大堆复杂的加解密函数(而且密码学本身很复杂,要写好它们并不容易)。
其主要库是以 C 语言所写成,实现了基本的加密功能,实现了 SSL 与 TLS 协议。
OpenSSL 整个软件包大概可以分成三个主要功能部分:
1)SSL 协议库;
2)应用程序;
3)密码算法库。
OpenSSL 的目录结构自然也是围绕这三个功能部分进行规划的。
OpenSSL 可以运行在 OpenVMS、 Microsoft Windows 以及绝大多数类 Unix 操作系统上。
5.2 具体来说
密钥和证书管理是 PKI 的一个重要组成部分,OpenSSL 为之提供了丰富的功能,支持多种标准。
OpenSSL 实现了 ASN.1 的证书和密钥相关标准,提供了对证书、公钥、私钥、证书请求以及 CRL 等数据对象的 DER、PEM 和 BASE64 的编解码功能。
OpenSSL 提供了产生各种公开密钥对和对称密钥的方法、函数和应用程序,同时提供了对公钥和私钥的 DER 编解码功能。并实现了私钥的 PKCS#12 和 PKCS#8 的编解码功能。
OpenSSL 在标准中提供了对私钥的加密保护功能,使得密钥可以安全地进行存储和分发。
在此基础上,OpenSSL 实现了对证书的 X.509 标准编解码、PKCS#12 格式的编解码以及 PKCS#7 的编解码功能。并提供了一种文本数据库,支持证书的管理功能,包括证书密钥产生、请求产生、证书签发、吊销和验证等功能。
5.3 发展历程
OpenSSL 计划在 1998 年开始,其目标是发明一套自由的加密工具,在互联网上使用。
OpenSSL 以 Eric Young 以及 Tim Hudson 两人开发的 SSLeay 为基础,随着两人前往 RSA 公司任职,SSLeay 在 1998 年 12 月停止开发。因此在 1998 年 12 月,社群另外分支出 OpenSSL,继续开发下去。
▲ 上图为 Tim Hudson
OpenSSL 管理委员会当前由 7 人组成有 13 个开发人员具有提交权限(其中许多人也是 OpenSSL 管理委员会的一部分)。只有两名全职员工(研究员),其余的是志愿者。
该项目每年的预算不到 100 万美元,主要依靠捐款。 TLS 1.3 的开发由 Akamai 赞助。
5.4 下载方法
OpenSSL 可以从其官网上下载,地址是:/source/index.html,感兴趣的读者可以自行下载安装研究。
6、各类证书
6.1 证书类型
操作过证书的朋友可能会对各种证书类型眼花缭乱,典型的体现就是各种不同的证书扩展名上,一般来说会有 DER、CRT、CER、PEM 这几种证书的扩展名。
以下是最常见的几种:
1)DER 文件:表示证书的内容是用二进制进行编码的;
2)PEM 文件:是一个文本文件,其内容是以“ - BEGIN -” 开头的,Base64 编码的字符;
3)CRT 和 CER 文件:基本上是等价的,他们都是证书的扩展,也是文本文件,不同的是 CRT 通常用在 liunx 和 unix 系统中,而 CER 通常用在 windows 系统中。并且在 windows 系统中,CER 文件会被 MS cryptoAPI 命令识别,可以直接显示导入和/或查看证书内容的对话框;
4)KEY 文件:主要用来保存 PKCS#8 标准的公钥和私钥。
6.2 常用 OpenSSL 命令
下面的命令可以用来查看文本证书内容:
openssl x509 -incert.pem -text -noout
openssl x509 -incert.cer -text -noout
openssl x509 -incert.crt -text -noout
下面的命令可以用来查看二进制证书内容:
openssl x509 -incert.der -inform der -text -noout
下面是常见的 PEM 和 DER 相互转换。
PEM 到 DER 的转换:
openssl x509 -incert.crt -outform der-out cert.der
DER 到 PEM 的转换:
openssl x509 -incert.crt -inform der -outform pem -out cert.pem
补充说明:上述命令中用到的 openssl 程序,就是本文中提到的 OpenSSL 开源库提供的程序。
7、Netty 中的聊天加密代码示例
7.1 关于 Netty
Netty 是一个 Java NIO 技术的开源异步事件驱动的网络编程框架,用于快速开发可维护的高性能协议服务器和客户端,事实上用 Java 开发 IM 系统时,Netty 是几乎是首选。
有关 Netty 的介绍我就不啰嗦了,如果不了解那就详读以下几篇:
《史上最强Java NIO入门:担心从入门到放弃的,请读这篇!》
《Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!》
《新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析》
《史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战》
基它有关 Netty 的重要资料:
7.2 启动 SSL Server 代码示例
事实上这个标题是不对的,Netty 中启动的 server 还是原来那个 server,只是对发送的消息进行了加密解密处理。也就是说添加了一个专门进行 SSL 操作的 Handler。
netty 中代表 ssl 处理器的类叫做SslHandler,它是SslContext工程类的一个内部类,所以我们只需要创建好 SslContext 即可通过调用 newHandler 方法来返回 SslHandler。
让服务器端支持 SSL 的代码:
ChannelPipeline p = channel.pipeline();
SslContext sslCtx = SslContextBuilder.forServer(...).build();
p.addLast("ssl", sslCtx.newHandler(channel.alloc()));
让客户端支持 SSL 的代码:
ChannelPipeline p = channel.pipeline();
SslContext sslCtx = SslContextBuilder.forClient().build();
p.addLast("ssl", sslCtx.newHandler(channel.alloc(), host, port));
netty 中 SSL 的实现有两种方式,默认情况下使用的是 OpenSSL,如果 OpenSSL 不可以,那么将会使用 JDK 的实现。
要创建 SslContext,可以调用SslContextBuilder.forServer或者SslContextBuilder.forClient方法。
这里以 server 为例,看下创建流程。
SslContextBuilder 有多种 forServer 的方法,这里取最简单的一个进行分析:
publicstaticSslContextBuilder forServer(File keyCertChainFile, File keyFile) {
returnnewSslContextBuilder(true).keyManager(keyCertChainFile, keyFile);
}
该方法接收两个参数:
1)keyCertChainFile 是一个 PEM 格式的 X.509 证书文件;
2)keyFile 是一个 PKCS#8 的私钥文件。
熟悉 OpenSSL 的童鞋应该知道使用 openssl 命令可以生成私钥文件和对应的自签名证书文件。
具体 openssl 的操作可以查看我的其他文章,这里就不详细讲解了。
除了手动创建证书文件和私钥文件之外,如果是在开发环境中,大家可能希望有一个非常简单的方法来创建证书和私钥文件,netty 为大家提供了SelfSignedCertificate类。
看这个类的名字就是知道它是一个自签名的证书类,并且会自动将证书文件和私钥文件生成在系统的 temp 文件夹中,所以这个类在生产环境中是不推荐使用的。默认情况下该类会使用 OpenJDK's X.509 来生成证书的私钥,如果不可以,则使用 Bouncy Castle 作为替代。
7.3 启动 SSL Client 代码示例
同样的在 client 中支持 SSL 也需要创建一个 handler。
客户端的 SslContext 创建代码如下:
// 配置 SSL.
finalSslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
上面的代码我们使用了一个InsecureTrustManagerFactory.INSTANCE 作为 trustManager。
什么是 trustManager 呢?
当客户端和服务器端进行 SSL 连接的时候,客户端需要验证服务器端发过来证书的正确性。
通常情况下,这个验证是到 CA 服务器中进行验证的,不过这样需要一个真实的 CA 证书环境,所以在测试中,我们使用 InsecureTrustManagerFactory,这个类会默认接受所有的证书,忽略所有的证书异常。
当然:CA 服务器也不是必须的,客户端校验的目的是查看证书中的公钥和发送方的公钥是不是一致的,那么对于不能联网的环境,或者自签名的环境中,我们只需要在客户端校验证书中的指纹是否一致即可。
netty 中提供了一个FingerprintTrustManagerFactory类,可以对证书中的指纹进行校验。
该类中有个 fingerprints 数组,用来存储安全的授权过的指纹信息。通过对比传入的证书和指纹,如果一致则校验通过。
使用 openssl 从证书中提取指纹的步骤如下:
openssl x509 -fingerprint -sha256 -inmy_certificate.crt
8、小结一下
上面我们对 Netty 聊天用到的加密技术和相关概念进行了梳理,我来简单这些概念之间的关系。
这些概念之间的关系,简单来说就是:
1)PKI:是一套加密体系和标准的合集,它是理论方案;
2)SSL:是利用了 PKI 理论体系,针对 Socket 网络这个场景设计的一套安全通信标准,属于是 PKI 的一个具体应用场景;
3)OpenSSL:是 PKI 体系及 SSL 标准的算法和代码实现,它包括了具体的开源代码、工具程序等;
4)各种证书:是在 SSL 或其它基于 PKI 体系的安全协议标准中需要使用的到一些加密凭证文件等。
而具体到 Netty 中的聊天加密,那就是应用了上述的 PKI 体系,基于 SSL 协议,在 OpenSSL 等开源库的帮助下实现的安全程序。
9、参考资料
[4] OpenSSL是什么软件
[5] netty系列之对聊天进行加密
[6] 跟着源码学IM
[7] 基于Netty,从零开发IM
[9] 快速理解TCP协议一篇就够
学习交流:
- 移动端 IM 开发入门文章:《新手入门一篇就够:从零开发移动端IM》
- 开源 IM 框架源码:https://github.com/JackJiang2011/MobileIMSDK(备用地址点此)
(本文已同步发布于:http://www.52im.net/thread-4104-1-1.html)
评论