1. 概述

对称加密算法在加密和解密时使用的是同一个密钥,为了解决信息公开传送和密钥管理的问题,于是提出了一种新的密钥交换协议,这种协议允许在不安全的媒体上的通讯双方交换信息、安全地达成一致的密钥系统,这就是非对称加密(公钥加密)。之所以称为非对称加密,是因为使用非对称加密算法时,加密和解密使用的是不同的密钥。这两个密钥分别是私钥(private key)和公钥(public key)。

常用的非对称加密算法有:RSA、ECC

2. 特点

  • 使用公钥加密后,只有对应的私钥才能解密
  • 使用私钥加密后,只有对应的公钥才能机密
  • 算法强度高,不适合加密较大的数据
  • 数据传输的安全性高于对称加密算法

3. RSA

RSA是一种非对称加密算法,它的名字是由它的三位开发者,即RonRivest、AdiShamir和LeonardAdleman 的姓氏的首字母组成的(Rivest-Shamir-Leonard)。RSA的密钥长度推荐为1024位。

RSA的基本原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

3.1 加密流程

RSA的加密可以用下面这个公式来表示:

密文 = 明文^E mod N

RSA的加密就是对明文代表数字的E次方取模。其中E和N就是RSA加密的密钥,E和N组合就是公钥了。那么如何确定E和N呢?

N的求法:准备两个很大的质数P、Q,然后将这两个数相乘,其结果就是N。N = P * Q

E的求法:首先计算N的欧拉函数(小于N的正整数中与N互质的数的数目)得到L,L = (P-1) * (Q-1),然后在1和L之间选取一个和L互质的数,这个数就是E。

最后就可以得到公钥KU = (E, N)。

3.2 解密流程

RSA的解密可以用下面这个公式来表示:

明文 = 密文^D mod N

RSA的解密就是对密文代表数字的D次方进行取模。这里使用的数字N和加密时使用的N是相同的。所以只有求出D就可以得到私钥。数D和数N组合起来就是解密的密钥,也就是私钥。

D的求法:E对于L的模反元素就是D。可以用这个公式快速求得D:E * D mod L = 1。

最后可以得到私钥KR = (D, N)

3.3 生成公私钥案例

  1. 选择一堆不相等且足够大的质数:P = 3、Q = 11
  2. 计算P和Q的乘积N:N = P * Q = 33
  3. 根据定理计算N的欧拉函数L:L = (P - 1) * (Q - 1) = 20
  4. 从1到L中选取一个与L互质的整数E:1 < E < 10,选取E = 3
  5. 计算E对于L的模反元素D:E * D mod L = 1,D = 7
  6. 求得公钥为:KU = (E, N) = (3, 33)
  7. 求得私钥为:KR = (D, N) = (7, 33)

3.4 使用案例

3.4.1 生成公私钥

// 生成公私钥
func GenerateKeyPair(bits int) {
	// 生成私钥
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		fmt.Println("generate private key err: ", err)
		return
	}

	// 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
	privateKeyDer := x509.MarshalPKCS1PrivateKey(privateKey)

	// 将私钥字符串设置到pem格式块中
	block := pem.Block{
		Type:    "PRIVATE KEY",
		Headers: nil,
		Bytes:   privateKeyDer,
	}

	// 写入文件
	file1, err := os.Create(privateKeyFile)
	if err != nil {
		fmt.Println("open file err: ", err)
		return
	}
	defer file1.Close()
	// 开始写入	
	err = pem.Encode(file1, &block)
	if err != nil {
		fmt.Println("pem encode err: ", err)
		return
	}

	// 从得到的私钥对象中将公钥信息取出
	publicKey := privateKey.PublicKey
	// 通过x509标准将得到的rsa公钥序列化为字符串
	publicKeyDer := x509.MarshalPKCS1PublicKey(&publicKey)
	// 将公钥字符串设置到pem格式块中
	block2 := pem.Block{
		Type:    "PUBLIC KEY",
		Headers: nil,
		Bytes:   publicKeyDer,
	}
	// 写入文件
	file2, err := os.Create(publicKeyFile)
	if err != nil {
		fmt.Println("open file err: ", err)
		return
	}
	err = pem.Encode(file2, &block2)
	if err != nil {
		fmt.Println("write public key err: ", err)
		return
	}
}

3.4.2 公钥加密

// 公钥加密
ffunc PublicKeyEncrypt(publicKeyFile string, plainText []byte) []byte {
	// 读取私钥
	publicKeyPem, err := ioutil.ReadFile(publicKeyFile)
	if err != nil {
		fmt.Println("read public key file err: ", err)
		return []byte{}
	}

	// 解码成x509格式,rest是未解码完的数据存储在这里
	block, _ := pem.Decode(publicKeyPem)
	// 得到私钥
	publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
	if err != nil {
		fmt.Println("x509 parse public key err: ", err)
		return nil
	}

	// 加密
	cipherData, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
	if err != nil {
		fmt.Println("public encrypt err: ", err)
		return nil
	}

	return cipherData
}

3.4.3 私钥解密

func PrivateKeyDecrypt(privateKeyFile string, cipherData []byte) []byte {
	// 读取私钥
	privateKeyPem, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		fmt.Println("read private file err: ", err)
		return nil
	}

	// 从数据中查找到DER格式的块
	privateKeyDer, _ := pem.Decode(privateKeyPem)

	// 解析一个pem格式的私钥
	privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyDer.Bytes)
	if err != nil {
		fmt.Println("parse private key err: ", err)
		return nil
	}

	// 解密
	plaintData, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherData)
	if err != nil {
		fmt.Println("private key decrypt err: ", err)
		return nil
	}

	return plaintData
}

测试结果:

python 非对称加密算法使用 非对称加密算法举例_ci

完整代码:https://github.com/bigzoro/cryptography/tree/main/asymmetricalCryption/rsa