netty 系列之: 对聊天进行加密
简介在之前的文章中,我们讲到了怎么使用 netty 建立聊天室,但是这样的简单的聊天室太容易被窃听了,如果想要在里面说点悄悄话是很不安全的,怎么办呢?学过密码学的朋友可能就想到了一个解决办法,聊天的时候对消息加密,处理的时候再对消息解密即可。
当然在 netty 中上述的工作都不需要我们手动来实现,netty 已经提供了支持 SSL 的 channel 供我们选择,一起来看看吧。
PKI 标准在讲 netty 的具体支持之前,我们需要先了解一下公钥和私钥的加密标准体系 PKI。PKI 的全称是 Public Key Infrastructure,也就是公钥体系。用于规范公钥私募进行加密解密的规则,从而便于不同系统的对接。
事实上 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 是个人消息交换与打包语法主要用来生成公钥和私钥。PKCS#10 是证书请求语法。
各类证书的后缀和转换操作过证书的朋友可能会对证书的后缀眼花缭乱,一般来说会有 DER、CRT、CER、PEM 这几种证书的后缀。
DER 表示证书的内容是用二进制进行编码的。
PEM 文件是一个文本文件,其内容是以“ – BEGIN -” 开头的,Base64 编码的字符。
CRT 和 CER 基本上是等价的,他们都是证书的扩展,也是文本文件,不同的是 CRT 通常用在 liunx 和 unix 系统中,而 CER 通常用在 windows 系统中。并且在 windows 系统中,CER 文件会被 MS cryptoAPI 命令识别,可以直接显示导入和/或查看证书内容的对话框。
KEY 文件,主要用来保存 PKCS#8 标准的公钥和私钥。
下面的命令可以用来查看文本证书内容:
openssl x509 -in cert.pem -text -nooutopenssl x509 -in cert.cer -text -nooutopenssl x509 -in cert.crt -text -noout 下面的命令可以用来查看二进制证书内容:
openssl x509 -in cert.der -inform der -text -noout 下面是常见的 PEM 和 DER 相互转换:
PEM 到 DER
openssl x509 -in cert.crt -outform der-out cert.der
DER 到 PEM
openssl x509 -in cert.crt -inform der -outform pem -out cert.pemnetty 中启动 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 的方法,这里取最简单的一个进行分析:
该方法接收两个参数,keyCertChainFile 是一个 PEM 格式的 X.509 证书文件,keyFile 是一个 PKCS#8 的私钥文件。
熟悉 OpenSSL 的童鞋应该知道使用 openssl 命令可以生成私钥文件和对应的自签名证书文件。
具体 openssl 的操作可以查看我的其他文章,这里就不详细讲解了。
除了手动创建证书文件和私钥文件之外,如果是在开发环境中,大家可能希望有一个非常简单的方法来创建证书和私钥文件,netty 为大家提供了 SelfSignedCertificate 类。
看这个类的名字就是知道它是一个自签名的证书类,并且会自动将证书文件和私钥文件生成在系统的 temp 文件夹中,所以这个类在生产环境中是不推荐使用的。默认情况下该类会使用 OpenJDK’s X.509 来生成证书的私钥,如果不可以,则使用 Bouncy Castle 作为替代。
netty 中启动 SSL client 同样的在 client 中支持 SSL 也需要创建一个 handler。客户端的 SslContext 创建代码如下:
// 配置 SSL.final SslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();上面的代码我们使用了一个 InsecureTrustManagerFactory.INSTANCE 作为 trustManager。什么是 trustManager 呢?
当客户端和服务器端进行 SSL 连接的时候,客户端需要验证服务器端发过来证书的正确性,通常情况下,这个验证是到 CA 服务器中进行验证的,不过这样需要一个真实的 CA 证书环境,所以在测试中,我们使用 InsecureTrustManagerFactory,这个类会默认接受所有的证书,忽略所有的证书异常。
当然,CA 服务器也不是必须的,客户端校验的目的是查看证书中的公钥和发送方的公钥是不是一致的,那么对于不能联网的环境,或者自签名的环境中,我们只需要在客户端校验证书中的指纹是否一致即可。
netty 中提供了一个 FingerprintTrustManagerFactory 类,可以对证书中的指纹进行校验。
该类中有个 fingerprints 数组,用来存储安全的授权过的指纹信息。通过对比传入的证书和指纹,如果一致则校验通过。
使用 openssl 从证书中提取指纹的步骤如下:
openssl x509 -fingerprint -sha256 -in my_certificate.crt 总结通过设置 client 和 server 端的 SSL handler,就可以实现客户端和服务器端的加密消息传输。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/12-netty-securechat/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/09904fe27e416c4afd8b77670】。文章转载请联系作者。
评论