基于非对称加密的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
进行通信,这也就是HTTPS
的含义。
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 Protocal
,是一个安全的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]
会发现已经不会再要求输入密码了,VPS
服务器已经成功添加客户端的公钥
,这个公钥
也被称为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
克隆的仓库依然需要输入账号密码。