1. 简介
在对称密码中,由于加密和解密的密钥是相同的,因此必须向接收者配送密钥。用于解密的密钥必须被配送给
接收者,这一问题称为密钥配送问题。如果使用非对称加密也可以称为公钥密码,则无需向接收者配送用于解
密的密钥,这样就解决了密钥配送问题。可以说非对称加密是密码学历史上最伟大的发明。
非对称加密中,密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密,接收者用解密密钥对
密文进行解密。要理解公钥密码,清楚地区分加密密钥和解密密钥是非常重要的。加密密钥是发送者加密时使
用的,而解密密钥则是接收者解密时使用的。
仔细思考一下加密密钥和解密密钥的区别,我们可以发现:
- 发送者只需要加密密钥
- 接收者只需要解密密钥
- 解密密钥不可以被窃听者获取
- 加密密钥被窃听者获取也没问题
也就是说,解密密钥从一开始就是由接收者自己保管的,因此只要将加密密钥发给发送者就可以解决密钥配送
问题了,而根本不需要配送解密密钥。
RSA是一种非对称加密算法,它的名字是由它的三位开发者,即RonRivest、AdiShamir和LeonardAdleman 的姓氏 的首字母组成的(Rivest-Shamir-Leonard)。
2. RSA加密
在RSA中,明文、密钥和密文都是数字。RSA的 加密过程可以用下列公式来表达,如下。
密文 = 明文^E mod N
也就是说,RSA的密文是对代表明文的数字的E次方求modN的结果。换句话说,就是将明文自己做E次乘法,然 后将其结果除以N求余数,这个余数就是密文。
加密公式中出现的两个数一一一E和N,到底都是什么数呢?RSA的加密是求明文的E次方modN,因此只 要知道E和N这两个数,任何人都可以完成加密的运算。所以说,E和N是RSA加密的密钥,也就是说, E和N的组 合就是公钥 。
不过,E和N并不是随便什么数都可以的,它们是经过严密计算得出的。
有一个很容易引起误解的地方需要大家注意一一E和N这两个数并不是密钥对(公钥和私钥的密钥对)。E和N两 个数才组成了一个公钥,因此我们一般会写成 “公钥是(E,N)” 或者 “公钥是{E, N}" 这样的形式,将E和N用括号 括起来。
3. RSA解密
RSA的解密和加密一样简单,可以用下面的公式来表达
明文 = 密文^D mod N
也就是说,对表示密文的数字的D次方求modN就可以得到明文。换句话说,将密文自己做D次乘法,再对其结果 除以N求余数,就可以得到明文。
这里所使用的数字N和加密时使用的数字N是相同的。 数D和数N组合起来就是RSA的解密密钥,因此D和N的组合 就是私钥 。只有知道D和N两个数的人才能够完成解密的运算。
在RSA中,加密和解密的形式是相同的。加密是求 "E次方的mod N”,而解密则是求 "D次 方的modN” 。
当然,D也并不是随便什么数都可以的,作为解密密钥的D,和数字E有着相当紧密的联系。否则,用E加密的结 果可以用D来解密这样的机制是无法实现的。
4. Go中生成公钥和私钥
- 生成私钥操作流程概述:
- 使用rsa中的GenerateKey方法生成私钥
- 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
- 将私钥字符串设置到pem格式块中
- 通过pem将设置好的数据进行编码, 并写入磁盘文件中
- 生成公钥操作流程:
- 从得到的私钥对象中将公钥信息取出
- 通过x509标准将得到 的rsa公钥序列化为字符串
- 将公钥字符串设置到pem格式块中
- 通过pem将设置好的数据进行编码, 并写入磁盘文件
- 生成公钥和私钥的源代码:
/*
* 生成RSA公钥和私钥并保存在对应的目录文件下
* 参数bits: 指定生成的秘钥的长度, 单位: bit
*/
func RsaGenKey(bits int, privatePath,pubulicPath string) error {
// 1. 生成私钥文件
// GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return err
}
// 2. MarshalPKCS1PrivateKey将rsa私钥序列化为ASN.1 PKCS#1 DER编码
derPrivateStream := x509.MarshalPKCS1PrivateKey(privateKey)
// 3. Block代表PEM编码的结构, 对其进行设置
block := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: derPrivateStream,
}
// 4. 创建文件
privateFile, err := os.Create(privatePath)
defer privateFile.Close()
if err != nil {
return err
}
// 5. 使用pem编码, 并将数据写入文件中
err = pem.Encode(privateFile, &block)
if err != nil {
return err
}
// 1. 生成公钥文件
publicKey := privateKey.PublicKey
derPublicStream, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
return err
}
block = pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: derPublicStream,
}
publicFile, err := os.Create(pubulicPath)
defer publicFile.Close()
if err != nil {
return err
}
// 2. 编码公钥, 写入文件
err = pem.Encode(publicFile, &block)
if err != nil {
panic(err)
return err
}
return nil
}
测试代码:
func testGenRSA() {
rsa.RsaGenKey(2048, "privateKey.pem","pubulicKey.pem")
}
5. Go中使用RSA
5.1. 操作步骤
- 公钥加密
- 将公钥文件中的公钥读出, 得到使用pem编码的字符串
- 将得到的字符串解码
- 使用x509将编码之后的公钥解析出来
- 使用得到的公钥通过rsa进行数据加密
- 公钥解密
- 将私钥文件中的私钥读出, 得到使用pem编码的字符串
- 将得到的字符串解码
- 使用x509将编码之后的私钥解析出来
- 使用得到的私钥通过rsa进行数据解密
5.2. 代码实现
- RSA公钥加密
/*
* RSA公钥加密
*/
func RSAEncrypt(src []byte, filename string) ([]byte, error) {
// 根据文件名读出文件内容
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
// 从数据中找出pem格式的块
block, _ := pem.Decode(buf)
if block == nil {
return nil, err
}
// 解析一个der编码的公钥
publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
return nil, err
}
// 公钥加密
result, _ := rsa.EncryptPKCS1v15(rand.Reader, publicKey, src)
return result, nil
}
- RSA私钥解密
/*
* RSA私钥解密
*/
func RSADecrypt(src []byte, filename string) ([]byte, error) {
// 根据文件名读出内容
file, err := os.Open(filename)
if err != nil {
return nil,err
}
defer file.Close()
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
// 从数据中解析出pem块
block, _ := pem.Decode(buf)
if block == nil {
return nil,err
}
// 解析出一个der编码的私钥
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
// 私钥解密
result, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, src)
if err != nil {
return nil,err
}
return result,nil
}
- 测试代码:
func testRSA() {
msg := "二愣子抗日"
cipherText, _:= rsa.RSAEncrypt([]byte(msg), "publicKey.pem")
fmt.Println(string(cipherText))
plainText, _:= rsa.RSADecrypt(cipherText, "privateKey.pem")
fmt.Println(string(plainText))
}