基于非对称加密的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
的前身,TLS1.0
通常被标记为SSL 3.1
。它们位于TCP/IP
之上,HTTPS
之下,HTTPS
实际就是HTTP over TLS
或HTTP over SSL
。相比HTTP
直接通过TCP/IP
建立通信,HTTPS
是与下层SSL
通信,即先用SSL
建立安全通信线路之后再用HTTP
进行通信。
HTTP
和HTTPS
本质向下发送的都是明文,只不过HTTP
数据包直接被TCP/IP
传输,而HTTPS
则是将数据包发给SSL
,加密后再通过TCP/IP
传输,其中基于证书
的身份验证也是由SSL
完成的。
HTTPS证书
HTTPS
使用非对称加密算法
建立前期的安全通信通道,服务器持有私钥
,建立连接时将公钥
发给客户端,然后双方就能进行加密通信。
但是服务器公钥
在传输过程中可能会因攻击被替换掉,如何证明收到的公钥
就是预想的服务器公钥
呢?这就需要使用由数字证书认证机构
(Certificate Authority
即CA
)颁发的数字证书
,该机构必须具备权威性并被客户端和服务器双方都认可。
服务器(网站)要使用HTTPS
,必须向CA
提出认证申请,如果CA
通过线上或线下验证申请者身份合法,服务器可信,就会为之生成一个Certificate数字证书
。证书
是一个文件,包含申请者公钥
、身份信息、签发机构CA
信息、有效时间、序列号等明文,同时包含一个用于验证这些信息是否被篡改的数字签名
。
签名
生成方式:使用散列函数(一般是SHA-256
)计算该明文信息的Message Digest
摘要,然后用 CA
的私钥
对摘要加密,密文即签名
。
建立HTTPS
通信时服务器会把证书
发送给客户端,客户端预装很多CA
机构的根证书
,包含每一个CA
的公钥
,通过公钥
来解密所接收证书
里的数字签名
并验证。如果正确则认为证书
里的服务器公钥
确实由CA
认证且未被篡改,因为只有CA
拥有私钥
,其它人无法伪造能被客户端验证的签名
(引申MITM中间人攻击)。
这样一来,客户端持有公钥
,服务器持有私钥
,即可建立安全的加密通信。即使在建立连接的阶段存在中间人,一旦客户端拿到证书
里的公钥
,它向服务器发送的下一条信息就是使用公钥
加密后的密文,而中间人没有服务器私钥
无法解密,也就看不“懂”后续的往来信息,这就是HTTPS
安全的原因。
加密通信双方都必须对数据包进行实时加密/解密,而非对称加密算法
比对称加密算法
更复杂,对资源和算力消耗也更多,所以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_rsa
和id_rsa.pub
是本机生成的一组非对称加密
的私钥
和公钥
。
authorized_keys
是本机在作为服务器时保存的远程客户端公钥
合集,当客户端登录时:
- 服务器使用
公钥
加密一个随机数R
,得到密文pubKey(R)
,发送给客户端。 - 客户端使用
私钥
解密得到R
,对R
和本次对话的SessionKey
进行MD5
计算,得到数据Digest1
,发送给服务器。 - 服务器同样对
R
和本次对话的SessionKey
进行MD5
计算,得到数据Digest2
,比较收到的Digest1
和自己计算出来的Digest2
是否相同,来确定是否允许客户端登录。
实例
首先使用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
2个文件,我有一台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 文件中的公钥导入到指定host的ssh服务器上
ssh-copy-id -i ~/.ssh/id_rsa [host]
此外,在GitHub
的Personal settings
->SSH and GPG keys
中也可以添加自己的SSH Key
,即可无密码远程控制git
仓库。
不过前提是仓库必须是登录GitHub
后通过SSH URL
克隆下来的,使用HTTPS URL
克隆的仓库依然需要输入账号密码。