理解非对称加密算法
概述
非对称加密算法是一种保护数据及验证数据合法性的算法。在非对称算法中,接收方生成一对密钥,即私钥和公钥。接收方将公钥发送给发送方。发送方用收到的公钥对数据加密,再发送给接收方。接收方收到数据后,使用自己的私钥解密。由于公钥加密的数据必须用对应的私钥才能解密,而私钥又只有接收方自己知道,这样就保证了数据传输的安全性。常用的非对称加密算法有 RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等。
此外,非对称加密还有数字签名的功能。如果某一用户使用他的私钥加密明文,任何人都可以用该用户的公钥解密密文;由于私钥只由该用户自己持有,故可以肯定该信息必定出自于该用户
某些文章称"公钥加密的信息只能用私钥解密,私钥加密的信息只能用公钥解密"的说法是不准确的。所谓的"私钥加密的信息只能用公钥解密"应当指私钥签名、公钥验证
简单来说,非对称加密即公钥加密,私钥解密和私钥签名,公钥验证。私钥需要保密,公钥需要给别人用。
ssh免密登录的流程
生成公钥和私钥
ssh-keygen
在命令的执行过程中会要求输入passphrase。passphrase是对私钥进行加密的,简单来说就是使用私钥时需要输入密码,这个密码的值就是passphrase。
直接设置为空即可。
私钥默认生成在~/.ssh/id_rsa目录下,公钥默认生成在~/.ssh/id_rsa.pub目录下。
把公钥发送给服务器
使用ssh-copy-id 服务器用户名@服务器ip将公钥发送给指定服务器。
ssh-copy-id root@10.211.55.10
公钥会被追加到服务器到~/.ssh/authorized_keys文件中。这个文件存储了允许访问该用户账户的公钥列表。当你使用私钥进行ssh认证时,服务器会检查你提供的私钥是否与其中一个公钥匹配,从而允许或拒绝你的访问请求。
免密登录
此时登录服务器就不需要密码了
ssh root@10.211.55.10
理解ssh免密登录的原理
从服务器回到本地。删除掉私钥文件,再尝试登录服务器,发现又需要输入密码了
brevin@Alticia .ssh % mv id_rsa ..
brevin@Alticia .ssh % ssh root@10.211.55.10
Authorized users only. All activities may be monitored and reported.
root@10.211.55.10's password:
恢复更改,尝试删除掉公钥文件,发现不影响免密登录
brevin@Alticia .ssh % mv id_rsa.pub ..
brevin@Alticia .ssh % ssh root@10.211.55.10
Welcome to 5.10.0-153.12.0.92.oe2203sp2.aarch64
root@OpenEuler:~
ssh免密登录本质上是在使用私钥进行签名,然后服务器使用相应的公钥进行验证。如果没有私钥,那么就无法完成签名,因此无法进行免密登录。
使用Go语言实现公私钥加密
由于go语言标准库齐全,下面所有代码都不需要引入外部依赖,方便复现,故本文代码均使用go实现
同时,为了使代码简洁,将忽略所有的错误处理
生成公私钥文件
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"os"
)
func main() {
var privateKey, _ = rsa.GenerateKey(rand.Reader, 2048)
os.WriteFile("private_key.pem", pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}), 0644)
os.WriteFile("public_key.pem", pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: x509.MarshalPKCS1PublicKey(&privateKey.PublicKey),
}), 0644)
}
运行上述代码,会在当前目录下生成私钥private_key.pem和公钥public_key.pem
使用公私钥进行加密解密
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"os"
)
func main() {
// 从文件中读取私钥
var privateKeyByte, _ = os.ReadFile("private_key.pem")
var privateKeyBlock, _ = pem.Decode(privateKeyByte)
var privateKey, _ = x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
// 从文件中读取公钥
var publicKeyByte, _ = os.ReadFile("public_key.pem")
var publicKeyBlock, _ = pem.Decode(publicKeyByte)
var publicKey, _ = x509.ParsePKCS1PublicKey(publicKeyBlock.Bytes)
// 使用公钥加密数据
var message = []byte("Hello World")
var cipherText, _ = rsa.EncryptPKCS1v15(rand.Reader, publicKey, message)
// 使用私钥解密数据
var plainText, _ = rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
println(string(plainText))
}
程序输出Hello World。可见私钥加密的内容被公钥正确解密了。
HTTPS中的非对称加密
HTTPS即Hyper Text Transfer Protocol Secure(超文本传输安全协议)。HTTPS使用HTTP进行通信,使用TLS(Transport Layer Security)来加密数据包。主要目的是在不安全的传输层协议之上,建立一个可靠的传输信道,需要解决的问题有防止信息被窃听,认证通信对象。
使用go语言实现HTTPS服务
使用mkcert生成证书
为了让Web服务器可以使用HTTPS协议,需要TLS证书。TLS证书的作用是验证公钥是否属于这个Web服务器。
首先使用mkcert生成TLS证书。mkcert是一个使用Go语言编写的,制作本地受信任证书的工具
可以在 https://github.com/FiloSottile/mkcert 上安装mkcert
配置完成后,使用如下命令生成证书
mkcert -install
mkcert localhost
第一个命令 -install 会安装根证书,用于信任由mkcert生成的证书。第二个命令localhost会生成一个针对 https://localhost 的自签名证书。
该命令会在当前目录下生成localhost.pem和localhost-key.pem文件。
localhost-key.pem:服务器私钥文件localhost.pem:证书文件,包含服务器的公钥及与之相关的东西
创建HTTPS服务
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, HTTPS!"))
})
http.ListenAndServeTLS(":443", "localhost.pem", "localhost-key.pem", nil)
}
运行上述代码,便可以正常访问 https://localhost

TLS证书
为了向用户提供HTTPS服务,服务提供方需要向CA(Certificate Authority 数字证书认证机构)申请TLS证书
TLS证书是一种遵循X.509规范的公钥证书,包含如下内容
- 版本:证书的版本号
- 序列号:表示证书的唯一标识符,由证书颁发机构分配
- 证书签名算法:证书签名算法名,为非对称加密算法,验证证书的真实性和完整性
- 颁发者:CA的信息
- 有效期:表示证书的有效时间范围,包括起始日期和截止日期
- 主题背景:证书的拥有者
- 证书持有者的公共密钥信息:表示证书拥有者的公钥及其相关参数,如算法、长度、指数等。
- 拓展程序:可选的字段
- 证书签名值:表示证书颁发机构对证书内容的数字签名,用于验证证书的完整性和真实性。
- 指纹:将证书内容用哈希算法计算得到的值,用于检测证书是否被篡改
TLS握手的过程
- 客户端发送它支持的密码算法的列表和不重数(nonce)
- 服务器从算法列表中选择一种对称算法(如AES),一种公钥算法(如具有特定密钥长度的RSA)和一种HMAC算法(如SHA256),把它的选择和服务器不重数返回给客户
- 客户验证证书,提取服务器的公钥,生成一个前主密钥(Pre-Master Secret PMS)。使用服务器的公钥加密PMS,并将加密后的PMS返回给服务器
- 客户和服务器使用前主密钥和不重数,调用相同的算法计算出主密钥(Master Secret MS)。主密钥用于生成会话密钥(每次通信都会重新生成会话密钥),此后,双方的报文均使用会话密钥对称加密。
- 客户向服务器发送它已发送和接收的所有握手报文的HMAC
- 服务器向客户发送它已经发送和接收的所有握手报文的HMAC
TLS握手即用非对称加密交换用于对称加密的会话密钥,用CA来验证非对称加密中的公钥。

浙公网安备 33010602011771号