“非对称加密也叫公钥密码: 使用公钥加密, 使用私钥解密”

下面我们来看一看使用公钥密码的通信流程。假设Alice要给Bob发送一条消息,Alice是发送者,Bob是接收者,而窃听者Eve能够窃所到他们之间的通信内容。

在公非对称加密通信中,通信过程是由接收者Bob来启动的。

  1. Bob生成一个包含公钥和私钥的密钥对。

    私钥由Bob自行妥善保管。

  2. Bob将自己的公钥发送给Alicea

    Bob的公钥被窃听者Eve截获也没关系。

    将公钥发送给Alice,表示Bob请Alice用这个公钥对消息进行加密并发送给他。

  3. Alice用Bob的公钥对消息进行加密。

    加密后的消息只有用Bob的私钥才能够解密。

    虽然Alice拥有Bob的公钥,但用Bob的公钥是无法对密文进行解密的。

  4. Alice将密文发送给Bobo

    密文被窃听者Eve截获也没关系。Eve可能拥有Bob的公钥,但是用Bob的公钥是无法进行解密的。

  5. Bob用自己的私钥对密文进行解密。

    请参考下图, 看一看在Alice和Bob之间到底传输了哪些信息。其实它们之间所传输的信息只有两个:Bob的公钥以及用Bob的公钥加密的密文。由于Bob的私钥没有出现在通信内容中,因此窃听者Eve无法对密文进行解密。

窃听者Eve可能拥有Bob的公钥,但是Bob的公钥只是加密密钥,而不是解密密钥,因此窃听者Eve就无法完成解密操作。

RSA加密

下面我们终于可以讲一讲非对称加密的代表—RSA的加密过程了。在RSA中,明文、密钥和密文都是数字。RSA的加密过程可以用下列公式来表达,如下。

密 文 = 明 文 E m o d N ( R S A 加 密 ) 密文=明文 ^ E mod N(RSA加密) 密文=明文EmodN(RSA加密)

也就是说,RSA的密文是对代表明文的数字的E次方求modN的结果。换句话说,就是将明文自己做E次乘法,然后将其结果除以N求余数,这个余数就是密文。

咦,就这么简单?

对,就这么简单。仅仅对明文进行乘方运算并求mod即可,这就是整个加密的过程。在对称密码中,出现了很多复杂的函数和操作,就像做炒鸡蛋一样将比特序列挪来挪去,还要进行XOR(按位异或)等运算才能完成,但RSA却不同,它非常简洁。

对了,加密公式中出现的两个数一一一E和N,到底都是什么数呢?RSA的加密是求明文的E次方modN,因此只要知道E和N这两个数,任何人都可以完成加密的运算。所以说,E和N是RSA加密的密钥,也就是说,E和N的组合就是公钥

不过,E和N并不是随便什么数都可以的,它们是经过严密计算得出的。顺便说一句,E是加密(Encryption)的首字母,N是数字(Number)的首字母

有一个很容易引起误解的地方需要大家注意一一E和N这两个数并不是密钥对(公钥和私钥的密钥对)。E和N两个数才组成了一个公钥,因此我们一般会写成 “公钥是(E,N)” 或者 “公钥是{E, N}" 这样的形式,将E和N用括号括起来。

现在大家应该已经知道,RSA的加密就是 “求E次方的modN",接下来我们来看看RSA的解密。

RSA解密

RSA的解密和加密一样简单,可以用下面的公式来表达:

明 文 = 密 文 D m o d N ( R S A 解 密 ) 明文=密文^DmodN(RSA解密) 明文=密文DmodN(RSA解密)

也就是说,对表示密文的数字的D次方求modN就可以得到明文。换句话说,将密文自己做D次乘法,再对其结果除以N求余数,就可以得到明文。

这里所使用的数字N和加密时使用的数字N是相同的。数D和数N组合起来就是RSA的解密密钥,因此D和N的组合就是私钥。只有知道D和N两个数的人才能够完成解密的运算。

大家应该已经注意到,在RSA中,加密和解密的形式是相同的。加密是求 "E次方的mod N”,而解密则是求 "D次方的modN”,这真是太美妙了。

当然,D也并不是随便什么数都可以的,作为解密密钥的D,和数字E有着相当紧密的联系。否则,用E加密的结果可以用D来解密这样的机制是无法实现的。

顺便说一句,D是解密〈Decryption)的首字母,N是数字(Number)的首字母

我们将上面讲过的内容整理一下,如下表所示。



生成私钥操作流程概述

  1. 使用rsa中的GenerateKey方法生成私钥
  2. 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
  3. 将私钥字符串设置到pem格式块中
  4. 通过pem将设置好的数据进行编码, 并写入磁盘文件中

生成公钥操作流程

  1. 从得到的私钥对象中将公钥信息取出
  2. 通过x509标准将得到 的rsa公钥序列化为字符串
  3. 将公钥字符串设置到pem格式块中
  4. 通过pem将设置好的数据进行编码, 并写入磁盘文件
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"os"
)

//生成rsa密钥对,并且保存到磁盘中
func GenerateRsaKey(keySize int) {
	//使用rsa中的GenerateKey生成私钥
	privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
	if err != nil {
		panic(err)
	}

	//2. 通过x509标准将得到的rsa私钥序列化为ASN.1的DER编码格式
	derText := x509.MarshalPKCS1PrivateKey(privateKey)

	//3. 要组织一个pem.block
	block := pem.Block{
		Type:  "rsa private key", //这个地方随便写什么
		Bytes: derText,           //ASN.1的DER编码格式
	}

	//4. pem编码
	file, err1 := os.Create("private.pem") //保存在磁盘的文件名
	if err1 != nil {
		panic(err1)
	}
	pem.Encode(file, &block)

	file.Close()

	//生成rsa公钥 ===================================
	//1. 从私钥中取出公钥
	publicKey := privateKey.PublicKey
	//2.使用x509标准序列化  注意参数传参为地址
	derstream, err2 := x509.MarshalPKIXPublicKey(&publicKey)
	if err2 != nil {
		panic(err2)
	}

	//3. 将得到的数据放到pem.block中
	block = pem.Block{
		Type:    "rsa public key",
		Headers: nil,
		Bytes:   derstream,
	}

	//pem编码
	file, err = os.Create("public.pem")
	if err != nil {
		panic(err)
	}
	pem.Encode(file, &block)

	file.Close()
}

//使用rsa公钥加密文件
func publicEncode(plainText []byte, filename string) []byte {
	//1. 读取公钥信息 放到data变量中
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	stat, _ := file.Stat() //得到文件属性信息
	data := make([]byte, stat.Size())
	file.Read(data)
	file.Close()
	//2. 将得到的字符串pem解码
	block, _ := pem.Decode(data)

	//3. 使用x509将编码之后的公钥解析出来
	pubInterface, err2 := x509.ParsePKIXPublicKey(block.Bytes)
	if err2 != nil {
		panic(err2)
	}
	pubKey := pubInterface.(*rsa.PublicKey)

	//4. 使用公钥加密
	cipherText, err3 := rsa.EncryptPKCS1v15(rand.Reader, pubKey, plainText)
	if err3 != nil {
		panic(err3)
	}
	return cipherText
}

//使用rsa私钥加密
func privateDecode(cipherText []byte, filename string) []byte {
	//1. 打开并读取私钥文件
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	stat, _ := file.Stat()
	data := make([]byte, stat.Size())
	file.Read(data)
	file.Close()
	//2. 将得到的字符串进行pem解码
	block, _ := pem.Decode(data)
	//3. 使用x509将编码之后的私钥解析出来
	privateKey, err3 := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err3 != nil {
		panic(err3)
	}
	//4. 使用私钥将数据解密
	plainText, err4 := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
	if err4 != nil {
		panic(err4)
	}
	return plainText
}

func main() {
	//生成rsa密钥对 1024 为密钥长度
	GenerateRsaKey(1024)
	//src表示明文,注意,明文越长,密钥的长度也要相应增加
	src := []byte("我是小崔,如果我死了,肯定不是自杀...")
	//使用rsa公钥加密
	cipherText := publicEncode(src, "public.pem")
	//使用rsa私钥解密
	plainText := privateDecode(cipherText, "private.pem")
	fmt.Println(string(plainText))
}

即可得到的公钥和私钥

-----BEGIN rsa private key-----
MIICXgIBAAKBgQC3swOLvQsBKQcb6o/medAanjuv3PMgK1jYvSD31IcMvU9N0nOv
qYgytSloHlu1GyKURBs5Vg3xVRsVaVgeRQnBtUEtaJNhT3+Qqe0Pynv+ifx2YidV
LyqgTzG+hHGp9uH2JAupkAqLzYwpw/rulOt2dq1J6hThT937FvpoH/KbBwIDAQAB
AoGBAIeeof+IkZdJsvXpNlPxmrIMIAS2GsilN/LLrotJXGsLWIEb3kzR3LuTA/7a
atpKLj1ICtFJtwF004n7PBMc5RXbeYQ5Sdb/6pYvlRWtkm4jaUZaB63SnbzL6RPp
NwCJjVGdJOdUG6yXeuHrvuTsbo/hlgoYWdr80Lb7Jf9ex6gBAkEA8tktzAU2+RkK
ue6elDUiytWk/XbEK3upEDp1L1FIJE2ioYRtrOxEEcBVbYXL+YkipL9rLf3C4k74
0STvprij1QJBAMGlzIyQyccnbBBF3TgYqKsMhdzTvemMWDX2Giq9nxxTgVaOqPYQ
u0W+/yN9gzlrJrKTOer4CEwkaCLNX2PbHWsCQQDZ6QVOODOu68iTNMo5JUD2DyVA
hyzZ89mthTcX4XDBmqRfGIytiUg/QX2mjFOOs35RpK4RE86m8cQVL3aX/MCNAkEA
qN2qeFGyg6cPB0nFVau7Oh4bhaxoCgfGzJelzeu5mnv/Z7nUAXApvvKFjy9ehW25
OzRD53EP20ZMQT0SmAN1rQJAQp949K4gjYLoIjmNKKulRix6HLDCVMsp132w/Hjv
kVpKJJ4IyDiPV8+6A3VAlxvSpjVusaR7Jq8VDiHBKEf5jQ==
-----END rsa private key-----

-----BEGIN rsa public key-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3swOLvQsBKQcb6o/medAanjuv
3PMgK1jYvSD31IcMvU9N0nOvqYgytSloHlu1GyKURBs5Vg3xVRsVaVgeRQnBtUEt
aJNhT3+Qqe0Pynv+ifx2YidVLyqgTzG+hHGp9uH2JAupkAqLzYwpw/rulOt2dq1J
6hThT937FvpoH/KbBwIDAQAB
-----END rsa public key-----

运行结果