11.SSH公私钥验证
约 2099 字大约 7 分钟...
SSH是否真的安全
背景
在配置完git公钥之后,首次使用git从仓库拉取代码时,都会有如下提示:
问题:
- 为什么要验证这个指纹?
- 如何验证这个指纹是否正确呢?
- SSH通信、代理服务是否真的安全呢?
公钥是否被篡改
理论基础
- 按照公、私钥的原理(TLS、SSH都适用):通信双方在握手时先通过公私钥加密的方式,协商出一个加密算法,然后进行对称加密密钥交换,后续传输数据时则使用对称加密算法
- 当通信双方存在中间人时,例如中间使用了代理服务,此时代理服务器可以获取到通信双方的所有数据,那么这样到底安全吗?
正常情况下,代理服务器虽然获取了双方的所有数据,但都是在TCP传输层的,因为缺少会话层的密钥,所以无法对数据进行解密
**但是假设通信双方在首次握手时,中间人伪造了双方的公钥,分发给双方。**即通信双方拥有的对方的公钥信息是篡改过的,那么此时通信双方的数据在经过中间人时,中间人就可以利用自己的私钥进行解密,获取所有数据,然后再通过公钥进行加密传输给对方。这样整个通信过程就是完全透明的,而通信双方完全不知道。
- 初步结论:
- 验证对端的公钥是否正确是很必要的一件事情
- 在TLS场景下,因为是HTTPS通信,客户端会可以通过CA验证服务器证书中的公钥是否正确。(验证时客户端通过CA的公钥,对服务器的公钥签名进行签名,比对从CA获取到的服务器的证书中的签名是否一致。这相当于套一层壳,这就有了CA证书链的概念)
在SSH场景下,访问某个服务器IP时,默认不会使用CA机构,那么首次连接时的服务器公钥验证就尤其重要
手动验证SSH服务器的公钥的方法
生成服务器公钥
ssh-keygen -t ed25519 -C "服务器标识" -f /etc/ssh/ssh_host_ed25519_key # 推荐使用ed25519,椭圆曲线加密算法
获取公钥的签名
ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
当客户端首次连接服务器
ssh user@server_ip The authenticity of host 'server_ip (xxx.xxx.xxx.xxx)' can't be established. ED25519 key fingerprint is SHA256:xxxxxxx. Are you sure you want to continue connecting (yes/no/[fingerprint])?
比对fingerprint是否一致
补充:
- 如果客户端已经连接过服务器,则可以比较known_host文件
ssh-keygen -l -f .ssh/known_hosts # 此处可能有服务器多个不同算法的多个签名
- 生成的fingerprint格式不一致 在服务器上生成的是:
256 6c:f1:78:09:f6:14:8f:5b:0d:6f:cd:7e:40:f3:c9:9c (ED25519)
这个是MD5格式的签名,可以尝试在客户端修改生成签名的算法指定为MD5ssh-keygen -l -f .ssh/known_hosts -E md5 # 此处可能有服务器多个不同算法的多个签名
- 如果客户端已经连接过服务器,则可以比较known_host文件
总结:
- 客户端的SSH公钥同样可以在服务端使用ssh-keygen 签名后进行验证
- 非对称加密的使用场景下,不仅要确保私钥的安全,还需要确保公钥交换时没有被篡改
- 在使用SSH等非对称加密的情况下,在确保双方公钥没被篡改的前提下,即使是存在代理服务(正向代理:公网WIFI、公司网络),
应用层
的通信依然是安全的 - 非对称加密推荐使用ed25519、ecdsa(椭圆曲线加密算法)而不是RSA、DSA,因为它安全性更高、性能更好,关键是
前向安全
延伸其他相关概念:
- 公钥:
- 隐私性:公开
- 作用: 1. 加密数据 2.验证签名
- 常见的公钥:
- TLS公钥 X.509标准
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw9T7Jq86Z4GZ+8R9J5U9 ... kZcA5m4+8JZu5wIDAQAB -----END PUBLIC KEY-----
- SSH公钥 RFC 4253标准
ssh-rsa AAAAB3NzaC1yc2E...(Base64)... user@host
- TLS公钥 X.509标准
- 私钥:
- 隐私性:保密
- 作用: 1. 解密数据 2.生成签名
- 常见的公钥:
- TLS私钥 PKCS#8 标准
-----BEGIN ENCRYPTED PRIVATE KEY----- # <mcfile name="private.key" path="/xxx/private.key"></mcfile> MIIFLTBXBgkqhkiG9w0BB... (Base64) -----END ENCRYPTED PRIVATE KEY-----
- SSH私钥 RFC 4716/PKCS#1
-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmIAAAAtAAAACmFlczI1Ni1jYmI ... -----END OPENSSH PRIVATE KEY-----
- TLS私钥 PKCS#8 标准
- CSR文件:
- 隐私性:不涉及,可公开
- 作用:CSR全称是证书签名请求,用于向CA申请数字证书
- 结构:
-----BEGIN CERTIFICATE REQUEST----- Base64编码数据(包含) ├── 主体信息(域名/组织) ├── 主体公钥(核心内容) └── 内容的数字签名(申请者私钥生成) -----END CERTIFICATE REQUEST-----
- 证书(PEM):
- 隐私性:公开
- 作用:PEM证书将公钥与持有者信息绑定,并由CA用私钥签名,从而确保公钥的真实性(通过CSR向CA申请或者自签名)
- 结构:
-----BEGIN CERTIFICATE----- Base64编码数据(包含) ├── 版本号 ├── 序列号 ├── 签名算法(如SHA256-RSA) ├── 颁发者(CA信息) ├── 有效期 ├── 主体信息(域名/组织) ├── 主体公钥(核心内容) └── CA的数字签名(用CA私钥生成) -----END CERTIFICATE-----
申请TLS证书:
- 还有一种自签名证书,可用于开发
验证TLS证书:
keys/
├── private.key # 最初生成(保密)
├── request.csr # 已提交(可存档)
└── certificate.pem # 从CA获得(部署用)
# 验证私钥与证书匹配(重要!)
openssl x509 -noout -modulus -in keys/certificate.pem | openssl md5
openssl rsa -noout -modulus -in keys/private.key | openssl md5
# 两个MD5值必须一致