基于非对称加密的 HTTPS 与 SSH

介绍 HTTPS 和 SSH 之前需补充一些《密码学》里典型的对称加密与非对称加密概念,它们是加密通信实现的基石。

对称加密又称私钥加密、共享密钥加密,加密/解密使用同一个或同一组密钥,常见对称加密算法有 DES、3DES、AES、Blowfish、IDEA、RC5 和 RC6。

非对称加密又称公开密钥加密,有 2 个互相不可推算的密钥组成,一个密钥加密仅且只能用另一个密钥解密。一般公开一个密钥称为公钥,另一个不公开称为私钥。如果有人持有公钥加密的密文,则该密文只有私钥持有者才能解密,其它人即使得到公钥也无法在数以年计的合理时间内解密得到明文。常见非对称加密算法有 RSA、EIGamal、背包算法和 Rabin。

在性能方面,因为非对称加密比对称加密复杂得多,所以对算力和资源要求更高。

HTTPS

传统 HTTP 存在明显的安全性问题:

  • 不验证通信方的身份,可能遭遇伪装。
  • 使用明文通信,内容可能被窃听。
  • 无法验证报文的完整性,可能被篡改。

为建立安全可靠的通信,HTTPS (HTTP Secure) 应运而生,对 HTTP 的缺点全面改进,相应的特点是:

  • 使用证书验证通信双方的身份。
  • 对通信内容进行加密。
  • 保护传输内容的完整性。

SSL 和 TLS

SSL (Secure Socket Layer) 即安全套接层,TLS (Transport Layer Security) 即安全传输层,SSL 是 TLS 的前身,TLS 1.0 通常被标记为 SSL 3.1。它们位于 TCP/IP 之上、HTTPS 之下,HTTPS 即 HTTP over TLS 或 HTTP over SSL。相比 HTTP 通过 TCP/IP 建立通信,HTTPS 是与下层 SSL 交互,SSL 建立的安全通信线路是关键。

https

HTTP 和 HTTPS 本质向下发送的都是明文,只不过 HTTP 数据包直接被 TCP/IP 传输,HTTPS 则是将数据包发给 SSL 加密后再通过 TCP/IP 传输,其中基于证书的身份验证由 SSL 完成。

HTTPS 证书

HTTPS 使用非对称加密算法建立前期的安全通信通道,服务器持有私钥,建立连接时将公钥发给客户端,然后双方进行加密通信。

但是服务器公钥在传输过程中可能会因攻击被替换,如何证明收到的公钥就是预期的服务器公钥呢?这需要使用由数字证书认证机构(Certificate Authority 即 CA)颁发的数字证书,该机构必须具备权威性能被客户端和服务器双方认可。

服务器(网站)使用 HTTPS 须向 CA 提出认证申请,CA 通过线上或线下验证申请者身份合法、服务器可信会为之生成 Certificate 数字证书。证书是一个文件,包含申请者公钥、身份信息、签发机构信息、有效时间、序列号等明文,同时包含一个用于验证信息是否被篡改的数字签名。

签名生成方式:使用散列函数(如 SHA-256)计算该明文信息的 Message Digest 数字摘要,再用 CA 私钥对摘要加密,密文即签名。

建立 HTTPS 通信时服务器会把证书发送给客户端,客户端预装很多 CA 机构的根证书,包含每一个 CA 的公钥,通过公钥解密所接收证书里的数字签名并验证。如果一致则认为证书里的服务器公钥确实由 CA 认证且未被篡改,因为只有 CA 拥有私钥,其它人无法伪造能被客户端验证的签名(引申 MITM 中间人攻击)。

客户端持有公钥、服务器持有私钥可建立安全的加密通信,即使连接建立阶段存在中间人,一旦客户端拿到证书里的公钥,它向服务器发送的下一条信息就是用公钥加密后的密文,中间人没有私钥无法解密自然看不“懂”后续信息,这是 HTTPS 安全的原因。

https ca

加密通信双方都必须对数据包进行实时加密/解密,而非对称加密比对称加密复杂消耗的计算资源更多,所以 HTTPS 在使用非对称加密建立安全信道后会传输一组对称加密的密钥,以优化性能。

SSH

SSH 即 Secure Shell Protocol,是一个用于连接远程服务器的安全 Shell 协议,与 HTTPS 类似,它也是基于非对称加密算法建立安全连接。

ssh

用于加密的服务器公钥在向客户端传输的过程中可能被攻击者替换,因此客户端必须验证所接收公钥的可信度。HTTPS 使用 CA 证书解决,SSH 则采用一种更为简单粗暴的方式,直接把收到的公钥呈现给用户,由用户确认是否信任。

ssh [username]@[host]

The authenticity of host 'xx.xx.xx.xx (xx.xx.xx.xx)' can't be established.
ECDSA key fingerprint is SHA256:OwuJrk7molnBEbP5VzzXdMBz53pUVYRMijyhnuEDKhY.
Are you sure you want to continue connecting (yes/no)?

第一次使用 SSH 连接远程服务器会看到服务器公钥经 SHA256 计算后的fingerprint,之所以使用fingerprint是因为公钥一般很长,难以直接比较。

用户如果信任公钥,输入账号密码后其会被存储到记录信任白名单的本地known_hosts文件里,下次连接就从白名单查询而无需重复确认。

known_hosts~/.ssh/目录下,不同操作系统的用户目录~不同,所以~/.ssh/目录的完整路径不尽相同。

# Linux
/home/[username]/.ssh/
# Windows
C://Users/[username]/.ssh/
# macOS
/Users/[username]/.ssh/

无密码登录

SSH 除支持普通的账号密码登录外也支持使用用户私钥无密码登录,或者说,经用户私钥加密的特定数据就是身份凭证。

.ssh/目录有4个文件:

文件名 说明
known_hosts 保存本机曾登录过的远程主机公钥
authorized_keys 保存要信任的能够远程登录本机的客户端公钥
id_rsa 本机的私钥
id_rsa.pub 本机的公钥

known_hosts已经解释过id_rsaid_rsa.pub是本机生成的一组非对称加密的私钥和公钥。

authorized_keys是本机作为服务器时保存的远程客户端公钥合集,当客户端登录时:

  • 服务器使用公钥加密一个随机数R,得到密文pubKey(R),发送给客户端。
  • 客户端使用私钥解密得到R,对R和本次对话的SessionKey进行 MD5 计算得到数据Digest1,发送给服务器。
  • 服务器同样对R和本次对话的SessionKey进行 MD5 计算得到数据Digest2,比较收到的Digest1和自己计算的Digest2是否相同判断是否允许客户端登录。

ssh key

实例

首先使用ssh-keygen生成客户端的私钥和公钥:

# 执行 ssh-keygen,按提示操作
ssh-keygen

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/apqx/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/apqx/.ssh/id_rsa.
Your public key has been saved in /Users/apqx/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:DFAUk2pnKMFCC917zj*********2XzP9PfAJc apqx@Guodongs-MBP
The key's randomart image is:
+---[RSA 2048]----+
|.*********  ..o.o|
| .o.+ .o.o+  .E.o|
|  .. ******o   .*|
|    o * =.  . =.o|
|     **********+*|
|      o ******* o|
|         .     = |
|              .  |
|                 |
+----[SHA256]-----+

用户.ssh/目录下会出现私钥id_rsa和公钥id_rsa.pub文件,我有一台 VPS 服务器,先使用账号密码登录:

# 按提示输入密码
ssh [username]@[host]

登录后将本机id_rsa.pub文件内容(即公钥)复制到 VPS 的.ssh/authorized_keys文件里:

vim ~/.ssh/authorized_keys

退出登录:

exit

再次登录该 VPS:

ssh [username]@[host]

会发现不要求输入密码,服务器已经成功添加客户端公钥,这个公钥被称为 SSH Key。

以上过程可使用ssh-copy-id工具一键实现:

# 自动把 ~/.ssh/id_rsa.pub 文件中的公钥导入指定服务器
ssh-copy-id -i ~/.ssh/id_rsa [host]

此外,在 GitHub 的 Personal settings -> SSH and GPG keys 中添加自己的 SSH Key 可以无密码远程控制 git 仓库。

github ssh

不过仓库必须是登录 GitHub 后通过 SSH URL 克隆下来的,使用 HTTPS URL 克隆的仓库依然需要输入账号密码。

github ssh

arrow_upward