SSL
SSL
SSL 即 Secure Sockets Layer,是提高信道安全性的协议。不使用 SSL 的 HTTP 通信,是不安全的,所有信息明文传输。
注意:SSL 应用于 socket,因此理论上 UDP、UnixSocket 都可以使用 SSL 协议。是现实反正我知道 openssl 可以运用于 TCP 和 UDP。查了资料显示,DTLS 协议实现了在 UDP 协议之上的 TLS 安全层。
安全通信应该具有下列所需的特性:
- 机密性:对数据进行加密,仅仅只有接收方和发送方能够对数据解密,其他方就算能获取数据也看不懂。(使用对称加密算法)
- 报文完整性:防止他人篡改数据。(可以用 MAC 来判断)
- 端点鉴别:鉴别另一方确实是我想要进行通信的一方。(可以用数字签名)
几个基本概念:
- 不重数
- 对称加密
- 非对称加密
- 哈希算法
- MAC 报文鉴别码
不重数解决的是,防止 SSL 握手阶段重放攻击,基本的思想来自 TCP 握手阶段的 随机序号(保证了上次建立连接发送的 SYN 包不会引起下次连接)。
对称加密,必须是双方都持有同一密钥,那么就能对数据加密与解密。
非对称加密,可以用私钥加密,公钥解密(数字签名);也可以用公钥加密私钥解密(加密通信)。
哈希算法,把非定常数据 hash 成定长字符串,无法解密,不可逆向,运算比较耗时。
MAC,即 Message Authentication Code 报文鉴别码。发送方发送在报文末尾带 MAC 的 package。MAC = h(m, s) 其中 h 为 hash 函数,m 为报文数据,s 为鉴别密钥;接收方用 鉴别密钥重新计算一遍 MAC,如果和报文末尾的一致,那么就可以保证报文的完整性。
常见加密算法以及 Hash 函数:
对称加密:AES、DES、Blowfish、CAST、IDEA、RC2、RC5
非对称加密:DH、RSA、DSA、EC
Hash 函数:MD5、SHA1、SHA2
SSL 握手、通信、挥手的整个流程
1. SSL 握手
(1) 客户端发送它支持的加密算法列表以及一个不重数。
(2) 服务器从列表中选出一个对称加密算法,一个非对称加密算法,一个 MAC 算法。它把三种算法连同自己的数字证书以及自己的不重数发给客户端。
(3) 根据 CA 给的公钥解密数字签名,得到 hash 值。验证该 hash 值是否和服务器发过来的元素经过 hash 后的值一致,来进行端点鉴别。
(4) 客户端生成一个 前主密钥 PMS(Pre-Master Secret),并用服务器给的公钥加密该 PMS,然后发给服务器。
注意:这里双方并不直接使用 PMS 对数据进行加密;而是,客户端和服务器分别通过 PMS 和自己收到的不重数,使用相同的密钥导出函数计算出自己的主密钥 MS(Master Secret),然后把 MS 切片成一个数据加密密钥 S 和一个 MAC。这样总共得到四个密钥,客户端和服务器共享这 4 个密钥。然后后面的机密性和数据完整性都由这 4 个密钥来实现。
(5) optional
(6) optional
(7) 客户端发送一个加密后 “finished” 报文表示握手阶段结束。
(8) 服务端发送一个加密后 “finished” 报文表示握手阶段结束。
注意:后续,客户端和服务器各需发送一个所有握手报文的一个 MAC 用于保证数据完整性,使握手免受篡改危害。见自顶向下p414.
(9) 用 S 进行加密通信。用 MAC 保证数据完整性。
2. SSL 挥手
不能简单的用 TCP FIN 来标识 SSL 挥手,因为这样会造成 截断攻击,即破坏者会直接发送一个 TCP FIN 过早的结束会话。具体见自顶向下p414.
使用 openssl 建立 安全 TCP 信道
这是 TCP 的安全通信。
OpenSSL 的 API 很多,但并不是都会被使用到,如果需要查看某个 API 的详细使用方法可以阅读 API文档。
1.1 初始化 OpenSSL
OpenSSL 在使用之前,必须进行相应的初始化工作。在建立 SSL 连接之前,要为 Client 和 Server 分别指定本次连接采用的协议及其版本,目前能够使用的协议版本包括 SSLv2、SSLv3、SSLv2/v3 和 TLSv1.0。SSL 连接若要正常建立,则要求 Client 和 Server 必须使用相互兼容的协议。
接下来是 openssl 库初始化的代码,根据 OpenSSL 的不同版本调用了不同的 API 进行初始化。
1 |
|
1.2 创建 CTX
CTX 是 SSL 会话环境,建立连接时使用不同的协议,其 CTX 也不一样。创建 CTX 的相关 OpenSSL 函数:
1 | //客户端、服务端都需要调用 |
1.3 创建 SSL 套接字
在创建 SSL 套接字之前要先创建 Socket 套接字,建立 TCP 连接。创建 SSL 套接字相关函数:
1 | SSL *SSl_new(SSL_CTX *ctx); //创建一个SSL套接字 |
1.4 完成 SSL 握手
在这一步,我们需要在普通 TCP 连接的基础上,建立 SSL 连接。与普通流套接字建立连接的过程类似:Client 使用函数SSL_connect()【类似于流套接字中用的connect()】发起握手,而 Server 使用函数 SSL_ accept()【类似于流套接字中用的accept()】对握手进行响应,从而完成握手过程。两函数原型如下:
1 | int SSL_connect(SSL *ssl); |
握手过程完成之后,Client 通常会要求 Server 发送证书信息,以便对 Server 进行鉴别。其实现会用到以下两个函数:
1 | X509 *SSL_get_peer_certificate(SSL *ssl); //从SSL套接字中获取对方的证书信息 |
1.5 数据传输
经过前面的一系列过程后,就可以进行安全的数据传输了。在数据传输阶段,需要使用 SSL_read( ) 和 SSL_write( ) 来代替普通流套接字所使用的 read( ) 和 write( ) 函数,以此完成对 SSL 套接字的读写操作,两个新函数的原型分别如下:
1 | int SSL_read(SSL *ssl,void *buf,int num); //从SSL套接字读取数据 |
1.6 会话结束
当 Client 和 Server 之间的通信过程完成后,就使用以下函数来释放前面过程中申请的 SSL 资源:
1 | int SSL_shutdown(SSL *ssl); //关闭SSL套接字 |
引用
- 《计算机网络——自顶向下方法》
- 基于OpenSSL实现的安全连接
- SSL API文档
- Https协议详解
- HTTPS是大势所趋?看腾讯专家通过Epoll+OpenSSL在高并发压测机器人中支持https
- openssl 编程入门(含完整示例)
- SSL连接建立过程分析
- SSL socket 通讯详解
- HTTPS从原理到应用(三):SSL/TLS协议
- SSL/TLS 握手优化详解
- 非阻塞/异步(epoll) openssl
- 两个基于openssl的https client例子
- OpenSSL编程初探1 — 使用OpenSSL API建立SSL通信的一般流程简介
- OpenSSL编程初探2 — 关于证书文件的加载
- 基于 openssl 的 HTTPS 通信 C++ 实现