基于非对称加密的HTTPS与SSH

立泉

在介绍HTTPSSSH之前,必须补充一些《密码学》里典型的对称加密非对称加密概念,它们是加密通信得以实现的基石。

对称加密又称私钥加密共享密钥加密,即在加密和解密时必须使用同一个或同一组密钥,在加密通信中,要求双方必须都持有同样的密钥。常见的对称加密算法有DES3DESAESBlowfishIDEARC5RC6

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

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

HTTPS

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

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

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

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

SSL和TLS

SSL(Secure Socket Layer)即安全套接层TLS(Transport Layer Security)即安全传输层SSLTLS的前身,TLS1.0通常被标记为SSL 3.1,它们位于TCP/IP之上,HTTPS之下,HTTPS实际就是HTTP over TLSHTTP over SSL,相对于HTTP直接通过TCP/IP建立通信,HTTPS是与下层的SSL进行通信的,即用SSL建立安全通信线路之后再使用HTTP进行通信,这也就是HTTPS的含义。

https

HTTPHTTPS本质上向下发送的都是明文,只不过HTTP数据包直接被TCP/IP进行传输,而HTTPS则是将数据包发给SSL,它会保证明文被加密后再通过TCP/IP传输,其中使用证书进行身份验证也是由SSL完成的。

HTTPS证书

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

但是服务器的公钥在传输过程中可能会因攻击而被替换掉,如何证明收到的公钥就是预想的那台服务器的公钥呢?这就需要使用由数字证书认证机构Certificate AuthorityCA)颁发的数字证书了,该机构必须具备权威性并被客户端和服务器双方都认可,这是由证书的签发、验证原理所要求的。

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

签名的生成:使用散列函数(一般是SHA-256)计算该明文信息的Message Digest摘要,然后用 CA私钥对摘要加密,密文即签名

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

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

https ca

因为在加密通信中双方都必须对数据包进行实时的加密、解密,而非对称加密算法对称加密算法复杂得多,对资源和算力的消耗也更多,所以HTTPS在使用非对称加密建立安全信道后会传输一组对称加密密钥,再利用它进行加密通信以优化资源消耗。

SSH

SSHSecure Shell Protocal,是一个安全的Shell连接协议,一般用在连接远程服务器的场景中,和HTTPS类似,SSH也是基于非对称加密算法建立安全的通信连接。

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.pub2个文件,我有一台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]

此外,在GitHubPersonal settings->SSH and GPG keys中也可以添加自己的SSH Key,如此就可以很方便的无密码远程控制git仓库。

github ssh

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

github ssh

arrow_upward