理解非对称加密算法

概述

非对称加密算法是一种保护数据及验证数据合法性的算法。在非对称算法中,接收方生成一对密钥,即私钥和公钥。接收方将公钥发送给发送方。发送方用收到的公钥对数据加密,再发送给接收方。接收方收到数据后,使用自己的私钥解密。由于公钥加密的数据必须用对应的私钥才能解密,而私钥又只有接收方自己知道,这样就保证了数据传输的安全性。常用的非对称加密算法有 RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等。
此外,非对称加密还有数字签名的功能。如果某一用户使用他的私钥加密明文,任何人都可以用该用户的公钥解密密文;由于私钥只由该用户自己持有,故可以肯定该信息必定出自于该用户
某些文章称"公钥加密的信息只能用私钥解密,私钥加密的信息只能用公钥解密"的说法是不准确的。所谓的"私钥加密的信息只能用公钥解密"应当指私钥签名、公钥验证

简单来说,非对称加密即公钥加密,私钥解密和私钥签名,公钥验证。私钥需要保密,公钥需要给别人用。

ssh免密登录的流程

生成公钥和私钥

ssh-keygen

在命令的执行过程中会要求输入passphrasepassphrase是对私钥进行加密的,简单来说就是使用私钥时需要输入密码,这个密码的值就是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.pemlocalhost-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
chrome

TLS证书

为了向用户提供HTTPS服务,服务提供方需要向CA(Certificate Authority 数字证书认证机构)申请TLS证书
TLS证书是一种遵循X.509规范的公钥证书,包含如下内容

  • 版本:证书的版本号
  • 序列号:表示证书的唯一标识符,由证书颁发机构分配
  • 证书签名算法:证书签名算法名,为非对称加密算法,验证证书的真实性和完整性
  • 颁发者:CA的信息
  • 有效期:表示证书的有效时间范围,包括起始日期和截止日期
  • 主题背景:证书的拥有者
  • 证书持有者的公共密钥信息:表示证书拥有者的公钥及其相关参数,如算法、长度、指数等。
  • 拓展程序:可选的字段
  • 证书签名值:表示证书颁发机构对证书内容的数字签名,用于验证证书的完整性和真实性。
  • 指纹:将证书内容用哈希算法计算得到的值,用于检测证书是否被篡改

TLS握手的过程

  1. 客户端发送它支持的密码算法的列表不重数(nonce)
  2. 服务器从算法列表中选择一种对称算法(如AES),一种公钥算法(如具有特定密钥长度的RSA)和一种HMAC算法(如SHA256),把它的选择和服务器不重数返回给客户
  3. 客户验证证书,提取服务器的公钥,生成一个前主密钥(Pre-Master Secret PMS)。使用服务器的公钥加密PMS,并将加密后的PMS返回给服务器
  4. 客户和服务器使用前主密钥和不重数,调用相同的算法计算出主密钥(Master Secret MS)。主密钥用于生成会话密钥(每次通信都会重新生成会话密钥),此后,双方的报文均使用会话密钥对称加密。
  5. 客户向服务器发送它已发送和接收的所有握手报文的HMAC
  6. 服务器向客户发送它已经发送和接收的所有握手报文的HMAC

TLS握手即用非对称加密交换用于对称加密的会话密钥,用CA来验证非对称加密中的公钥。

posted @ 2023-08-15 16:57  BrevinZhang  阅读(287)  评论(0)    收藏  举报