rsa.generatekey 必须用 crypto/rand.reader 生成私钥,禁用 math/rand;密钥长度限1024/2048/3072/4096;加密须用 encryptoaep 或 encryptpkcs1v15(不可裸rsa),解密需匹配函数及 label,传输前 base64 编码,解密后必须校验明文长度与格式。

rsa.GenerateKey 生成密钥对时,私钥必须用 crypto/rand.Reader,不能用 rand.NewSource
Go 的 rsa.GenerateKey 要求传入一个加密安全的随机源,crypto/rand.Reader 是唯一被设计用于此场景的实现。用 math/rand 的 rand.New(rand.NewSource(time.Now().UnixNano())) 会导致私钥可预测,直接崩溃整个加密体系。
常见错误现象:panic: crypto/rsa: invalid prime: not prime 或更隐蔽地——密钥看似生成成功,但加密后无法解密,或在不同机器上行为不一致。
-
rsa.GenerateKey第二个参数是密钥长度(如 2048),必须是 1024、2048、3072 或 4096;小于 1024 会 panic - 生成的
*rsa.PrivateKey同时包含公钥(priv.PublicKey),无需单独导出公钥结构 - 若需 PEM 编码私钥,记得用
x509.MarshalPKCS8PrivateKey(推荐)而非x509.MarshalPKCS1PrivateKey,后者仅兼容老系统且不支持 3072+ 密钥
加密时必须用 rsa.EncryptOAEP 或 rsa.EncryptPKCS1v15,不能直接调用 Priv.Decrypt
RSA 不能像对称加密那样“把数据喂给函数就完事”。Go 标准库禁止裸 RSA 加密(即 textbook RSA),因为不加填充的加密在现实中等同于没加密——易受共模攻击、选择明文攻击等。
使用场景:对外发送敏感数据(如 AES 临时密钥)时,用公钥加密;服务端收到后用私钥解密。注意:RSA 本身只适合加密小数据(≤ 密钥长度/8 − 填充开销),别试图加密整个文件。
在 Next.js + Supabase 应用中集成自托管加密支付结账,支持 ETH、BTC、SOL、USDC、USDT 及 9 条链的25+ 币种,无需支付处理机构。
-
rsa.EncryptOAEP更现代、更安全,需要指定哈希(如sha256.New())和非空标签(哪怕只是[]byte("session")) -
rsa.EncryptPKCS1v15兼容性好,但已不推荐用于新项目;它不要求哈希,但对明文长度限制更死板(≤ 密钥字节数 − 11) - 解密必须用对应函数:
rsa.DecryptOAEP配EncryptOAEP,rsa.DecryptPKCS1v15配EncryptPKCS1v15;混用会返回crypto/rsa: decryption error
私钥解密失败常见原因:密文被 base64 或 hex 编码后未还原为原始字节
很多人把加密后的 []byte 直接转成 base64.StdEncoding.EncodeToString 发给前端,后端收回来却忘了用 base64.StdEncoding.DecodeString 还原——结果传给 rsa.DecryptOAEP 的是一串 ASCII 字符,不是原始密文,必然报错 crypto/rsa: decryption error 或 crypto/rsa: message too long。
性能影响:base64 编码会让密文体积膨胀约 33%,但这是必要代价;别为了省这点带宽跳过编码,更别用 string(ciphertext) 强转——UTF-8 不兼容二进制数据。
- 加密后存数据库或传 HTTP,务必先
base64编码;解密前必须先base64解码回[]byte - 如果用 JSON 传输,字段值应为 base64 字符串,而不是嵌套对象或数字数组
- 调试时可用
fmt.Printf("%x", ciphertext)快速确认是否为预期长度(如 2048 位密钥 → 密文固定为 256 字节)
生产环境必须校验 err != nil,尤其在 Decrypt 后检查明文长度是否合理
Go 的 RSA 解密函数即使失败也常返回 nil 错误 + 垃圾明文(比如全零切片)。这是因为某些攻击场景下,静默失败比 panic 更安全。所以光看 err == nil 不够,你还得验证输出。
容易被忽略的地方:解密返回的 plaintext 是 []byte,但它的内容可能是有效数据,也可能是填充垃圾。OAEP 模式下,明文长度不可控;PKCS#1 v1.5 下则严格等于你加密时传入的原文长度。因此,如果业务上知道原文该是 32 字节(比如一个 AES key),解密后必须显式检查 len(plaintext) == 32。
- 永远不要信任解密后的
plaintext内容,除非你同时校验了长度、格式(如是否为合法 UTF-8)、甚至附带的 MAC 或签名 -
rsa.DecryptOAEP的label参数必须和加密时完全一致,包括nil和空切片[]byte{}也不等价 - 如果密钥轮换,旧密文无法用新私钥解密——这不是 bug,是设计使然;解密逻辑里要预留多私钥尝试机制
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










