非对称加密之RSA(go语言)

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来解密这样的机制是无法实现的。


RSA加密解密.png

4. Go中生成公钥和私钥

  • 生成私钥操作流程概述:
  1. 使用rsa中的GenerateKey方法生成私钥
  2. 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
  3. 将私钥字符串设置到pem格式块中
  4. 通过pem将设置好的数据进行编码, 并写入磁盘文件中
  • 生成公钥操作流程:
  1. 从得到的私钥对象中将公钥信息取出
  2. 通过x509标准将得到 的rsa公钥序列化为字符串
  3. 将公钥字符串设置到pem格式块中
  4. 通过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. 操作步骤

  • 公钥加密
  1. 将公钥文件中的公钥读出, 得到使用pem编码的字符串
  2. 将得到的字符串解码
  3. 使用x509将编码之后的公钥解析出来
  4. 使用得到的公钥通过rsa进行数据加密
  • 公钥解密
  1. 将私钥文件中的私钥读出, 得到使用pem编码的字符串
  2. 将得到的字符串解码
  3. 使用x509将编码之后的私钥解析出来
  4. 使用得到的私钥通过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))
}