在Go语言的实际开发中,RSA-SHA数字签名常用于接口鉴权、数据防篡改等场景,其核心逻辑是先对原始数据做SHA系列哈希计算,再用RSA私钥对哈希值加密生成签名,验证时用公钥解密签名得到哈希值,和重新计算的原始数据哈希值比对一致则验证通过。

前置准备:生成RSA密钥对
首先需要生成可用的RSA密钥对,这里使用Go标准库的crypto/rsa和crypto/rand生成2048位长度的密钥,长度符合当前安全规范。
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
// 生成RSA密钥对并保存为PEM格式文件
func generateRSAKey(bits int) error {
// 生成私钥
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return err
}
// 将私钥编码为PKCS1格式
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privateBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// 保存私钥到文件
privateFile, err := os.Create("private.pem")
if err != nil {
return err
}
defer privateFile.Close()
err = pem.Encode(privateFile, privateBlock)
if err != nil {
return err
}
// 将公钥编码为PKIX格式
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
return err
}
publicBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
}
// 保存公钥到文件
publicFile, err := os.Create("public.pem")
if err != nil {
return err
}
defer publicFile.Close()
return pem.Encode(publicFile, publicBlock)
}
func main() {
err := generateRSAKey(2048)
if err != nil {
fmt.Println("生成密钥失败:", err)
return
}
fmt.Println("密钥生成成功")
}
核心实现:RSA-SHA签名生成
签名生成需要选择对应的SHA哈希算法,常用的有SHA256、SHA512等,这里以SHA256为例,完整流程如下:
- 读取原始待签名数据
- 计算原始数据的SHA256哈希值
- 使用RSA私钥对哈希值进行签名,签名算法选择
rsa.SignPKCS1v15 - 签名结果通常做Base64编码方便传输存储
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"os"
)
// 从PEM文件读取私钥
func loadPrivateKey(path string) (*rsa.PrivateKey, error) {
keyBytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}
block, _ := pem.Decode(keyBytes)
if block == nil {
return nil, fmt.Errorf("私钥PEM解码失败")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKey, nil
}
// RSA-SHA256签名
func rsaSha256Sign(data []byte, privateKey *rsa.PrivateKey) (string, error) {
// 计算SHA256哈希
hash := sha256.Sum256(data)
// 使用私钥签名
signBytes, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
if err != nil {
return "", err
}
// 签名结果做Base64编码
return base64.StdEncoding.EncodeToString(signBytes), nil
}
func main() {
// 待签名数据
originData := []byte("这是需要签名的测试数据")
// 加载私钥
privateKey, err := loadPrivateKey("private.pem")
if err != nil {
fmt.Println("加载私钥失败:", err)
return
}
// 生成签名
sign, err := rsaSha256Sign(originData, privateKey)
if err != nil {
fmt.Println("签名失败:", err)
return
}
fmt.Println("生成的签名:", sign)
}
核心实现:RSA-SHA签名验证
签名验证是签名的逆过程,需要用对应的公钥完成,流程和签名生成对应:
- 读取原始数据和待验证的签名
- 对原始数据重新计算SHA256哈希值
- 将Base64编码的签名解码为字节数组
- 使用RSA公钥解密签名得到哈希值,和重新计算的哈希值比对
package main
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"os"
)
// 从PEM文件读取公钥
func loadPublicKey(path string) (*rsa.PublicKey, error) {
keyBytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}
block, _ := pem.Decode(keyBytes)
if block == nil {
return nil, fmt.Errorf("公钥PEM解码失败")
}
publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
publicKey, ok := publicKeyInterface.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("公钥类型转换失败")
}
return publicKey, nil
}
// RSA-SHA256签名验证
func rsaSha256Verify(data []byte, signStr string, publicKey *rsa.PublicKey) (bool, error) {
// 解码Base64签名
signBytes, err := base64.StdEncoding.DecodeString(signStr)
if err != nil {
return false, err
}
// 计算原始数据的SHA256哈希
hash := sha256.Sum256(data)
// 验证签名
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hash[:], signBytes)
if err != nil {
return false, nil
}
return true, nil
}
func main() {
originData := []byte("这是需要签名的测试数据")
// 这里假设sign是之前生成的签名字符串
sign := "之前生成的Base64签名字符串"
// 加载公钥
publicKey, err := loadPublicKey("public.pem")
if err != nil {
fmt.Println("加载公钥失败:", err)
return
}
// 验证签名
valid, err := rsaSha256Verify(originData, sign, publicKey)
if err != nil {
fmt.Println("验证过程出错:", err)
return
}
if valid {
fmt.Println("签名验证通过")
} else {
fmt.Println("签名验证失败")
}
}
常见实践注意事项
哈希算法选择
不要使用SHA1这类已被证明不安全的哈希算法,优先选择SHA256及以上长度的算法,根据安全需求可以选择SHA384、SHA512等。
密钥长度要求
RSA密钥长度建议不低于2048位,如果是高安全场景建议使用4096位,避免使用1024位及以下长度的密钥。
数据编码规范
签名和验证时,原始数据的编码必须一致,比如都使用UTF-8编码,否则会出现哈希值不一致导致验证失败的问题。如果数据需要传输,签名结果建议做Base64编码,避免二进制数据在传输过程中出现损坏。
错误处理规范
不要忽略签名和验证过程中的错误,比如私钥加载失败、签名计算失败等情况都需要做对应的错误处理,避免程序出现不可预期的行为。
注意:RSA签名本身不提供加密功能,只是保障数据的完整性和签名者身份的真实性,如果需要同时保障数据机密性,需要额外做加密处理。