Go 语言中如何实现 RSA 加解密与签名验证

程一诺
程一诺
发布时间:2026-08-25 | 67人浏览过
原创

前言

在 Go 里接入 RSA,难点通常不在 API 本身,而在于公私钥怎么生成、加解密和签名验签到底该怎么分工。本文基于 github.com/forgoer/openssl 这套更省代码的封装,把三个高频场景拆开说明,并保留可直接上手的示例,帮助你快速判断该选哪组接口、密钥该怎样流转。

在 Go 项目里接入 RSA,很多人第一反应是直接啃标准库,但真正上手时,往往先卡在密钥生成、文件读写和加解密与签名的职责区分上。本文换一种更适合快速落地的写法,基于 github.com/forgoer/openssl 这个库,把 RSA 的常见用法拆成生成密钥、加解密和签名验签三部分,读完可以直接判断自己该用哪一组 API、数据该如何流转。

为什么用这个库实现 RSA

RSA 属于典型的非对称加密算法,和对称加密不同,它使用一对密钥协同工作:公钥通常用于加密,私钥用于解密;私钥也可以用于签名,而公钥用于验证签名。

在 Go 里,基于标准库实现 RSA 并不难,但样板代码相对多。如果你的目标是尽快完成功能验证,或者在业务代码里减少密钥处理的细节,这个封装库会更直接一些:

go get https://github.com/forgoer/openssl

文中的示例都基于这个库展开,涉及的命令、函数名和算法参数都保持原样,便于直接复用。

如何生成 RSA 公私钥

密钥既可以写入文件,也可以写入内存中的 Buffer。核心要求只有一个:目标对象实现了 io.Writer 接口。

RSA 密钥生成流程示意,展示私钥生成、公钥导出与文件或 Buffer 输出方式
RSA 密钥是怎么生成出来的把“先生成私钥,再导出公钥”的关系画清楚,比单看代码更容易理解密钥来源。

下面这段代码演示了一个常见流程:先生成私钥文件 private.key,再从私钥内容推导出公钥文件 public.key。

import (
    "io/ioutil"
    "os"

    "github.com/forgoer/openssl"
)

func main() {

    // 创建私钥文件
    priFile, err := os.Create("private.key") // 也可以使用buffer, priBuf := bytes.NewBuffer(nil)
    if err != nil {
        panic(err)
    }
    defer priFile.Close()
    // 生成私钥到文件
    _ = openssl.RSAGenerateKey(1024, priFile)

    // 创建公钥文件
    pubFile, err := os.Create("public.key")
    if err != nil {
        panic(err)
    }
    defer priFile.Close()

    // 通过私钥生成公钥到文件
    priByte, _ := ioutil.ReadFile("private.key")
    _ = openssl.RSAGeneratePublicKey(priByte, pubFile)
}

这里有几个读代码时值得注意的点:

密钥位数已经在生成阶段确定

示例里调用的是 openssl.RSAGenerateKey(1024, priFile),也就是生成一个 1024 位的私钥。后续公钥是从这份私钥导出的,不需要单独指定位数。

私钥是整个流程的起点

公钥不是独立随机生成的,而是通过 openssl.RSAGeneratePublicKey(priByte, pubFile) 从私钥内容生成。这也解释了为什么代码会先读取 private.key,再写出 public.key。

RSA 两组核心操作对照图,区分公钥加密私钥解密与私钥签名公钥验签
别把加解密和签名验签混在一起把加解密和签名验签放在一张对照图里,能减少最常见的职责混淆。

文件不是唯一输出方式

如果你不想把密钥直接落盘,可以像注释里那样改用 Buffer。只要目标实现 io.Writer,这套写法就能复用到接口返回、临时内存处理或测试场景中。

加解密该怎么用

RSA 最容易记混的一组动作,就是“谁加密、谁解密”。在这个示例里,规则很明确:使用公钥加密,使用私钥解密。

    src := []byte("123456")
    pubByte, _ := ioutil.ReadFile("public.key")
    // 公钥加密
    dst, _ := openssl.RSAEncrypt(src, pubByte)

    priByte, _ := ioutil.ReadFile("private.key")
    // 私钥解密
    res, _ := openssl.RSADecrypt(dst, priByte)

    fmt.Println(string(res)) // 123456

从数据流转看,这段代码做了三件事:

先准备明文和公钥

明文数据是 []byte("123456"),公钥内容通过读取 public.key 获得,然后传入 openssl.RSAEncrypt(src, pubByte) 完成加密。

密文再交给私钥还原

加密结果保存在 dst 中,接着读取 private.key,调用 openssl.RSADecrypt(dst, priByte) 还原出原始数据。

输出结果用于确认流程是否打通

最后打印出的结果是 123456,说明这组“公钥加密、私钥解密”的链路已经跑通。对于刚接入 RSA 的场景,这种最小化验证方式很实用。

签名和验签与加解密有什么区别

很多初学者把“加解密”和“签名验签”混为一谈,但它们解决的问题并不相同。加解密关注的是数据内容能否被安全还原,签名验签关注的是数据是否被篡改、发送方身份是否可信。

在这个库里,对应关系是:私钥签名,公钥验证。

    // 私钥签名
    sign, err := openssl.RSASign([]byte("123456"), priByte, crypto.SHA256)
    if err != nil {
        panic(err)
    }

    // 公钥验证签名
    err = openssl.RSAVerify([]byte("123456"), sign, pubByte, crypto.SHA256)
    if err != nil {
        panic(err)
    }

签名使用私钥完成

这里调用的是 openssl.RSASign([]byte("123456"), priByte, crypto.SHA256)。也就是说,原始数据是 123456,签名时使用私钥,并指定哈希算法为 crypto.SHA256。

验签要使用相同数据和公钥

验证时传入的是原始数据、签名结果、对应公钥,以及同样的 crypto.SHA256。如果数据内容、签名值、公钥或哈希算法不匹配,openssl.RSAVerify 就会返回错误。

判断思路要和加解密分开

如果你的目的是“别人能不能读到内容”,看的是加解密流程;如果你的目的是“这份内容是不是我发的、有没有被改过”,看的是签名验签流程。两组 API 经常一起出现,但职责并不重合。

这套写法适合什么场景

如果你只是想在 Go 中快速完成 RSA 的接入,这个库确实能减少不少样板代码。文中用到的能力覆盖了最常见的三类需求:生成密钥、RSA 加解密、RSA 签名与验证。

除了 RSA,这个库还支持 AES、DES、sha1、Hmac-Sha1、sha256、Hmac-Sha256 等常用算法。对于需要统一处理多种加密与摘要能力的 Go 项目,这类封装库更容易快速接入并形成一致用法。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表