在 Go 项目里接入 RSA,很多人第一反应是直接啃标准库,但真正上手时,往往先卡在密钥生成、文件读写和加解密与签名的职责区分上。本文换一种更适合快速落地的写法,基于 github.com/forgoer/openssl 这个库,把 RSA 的常见用法拆成生成密钥、加解密和签名验签三部分,读完可以直接判断自己该用哪一组 API、数据该如何流转。
为什么用这个库实现 RSA
RSA 属于典型的非对称加密算法,和对称加密不同,它使用一对密钥协同工作:公钥通常用于加密,私钥用于解密;私钥也可以用于签名,而公钥用于验证签名。
在 Go 里,基于标准库实现 RSA 并不难,但样板代码相对多。如果你的目标是尽快完成功能验证,或者在业务代码里减少密钥处理的细节,这个封装库会更直接一些:
go get https://github.com/forgoer/openssl
文中的示例都基于这个库展开,涉及的命令、函数名和算法参数都保持原样,便于直接复用。
如何生成 RSA 公私钥
密钥既可以写入文件,也可以写入内存中的 Buffer。核心要求只有一个:目标对象实现了 io.Writer 接口。

下面这段代码演示了一个常见流程:先生成私钥文件 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。

文件不是唯一输出方式
如果你不想把密钥直接落盘,可以像注释里那样改用 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 项目,这类封装库更容易快速接入并形成一致用法。









