加粗样式golang实现的Rsa加密解密算法(go和java交互时双向加解密方案)
使用说明:
- public_java.key 是java生成的公钥,需要java方生成并发给go方保存。
- go_private.key和go_public.key 是go生成的公私钥。其中go_public.key文件需发给java方保存,java方用来验证go给的签名,同时java方加密内容时也会用到go_public.key。反之java方也是同样的道理
- 支持分段加密, 可以 解决消息过长问题
go
package utils
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"go-admin/common/client"
"go-admin/common/file_store"
"io"
"math"
mrand "math/rand"
"os"
"strconv"
"strings"
"time"
)
// 包级别初始化随机数生成器
func readKeyFromFile(filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("读取文件失败: %v", err)
}
return string(data), nil
}
// 修复:增强私钥加载函数,自动补全PEM标签并清理格式
func loadPrivateKey(privKeyStr string) (*rsa.PrivateKey, error) {
// 1. 清理空格和换行
privKeyStr = strings.TrimSpace(privKeyStr)
// 2. 自动补全PEM标签(处理Java可能缺失标签的情况)
if !strings.HasPrefix(privKeyStr, "-----BEGIN PRIVATE KEY-----") {
privKeyStr = "-----BEGIN PRIVATE KEY-----\n" + privKeyStr
}
if !strings.HasSuffix(privKeyStr, "-----END PRIVATE KEY-----") {
privKeyStr = privKeyStr + "\n-----END PRIVATE KEY-----"
}
// 3. 解码PEM
block, rest := pem.Decode([]byte(privKeyStr))
if block == nil {
// 调试信息:输出无法解码的内容(生产环境可删除)
fmt.Printf("PEM解码失败,原始内容: %s\n", privKeyStr)
fmt.Printf("剩余未解码内容: %s\n", string(rest))
return nil, fmt.Errorf("failed to decode PEM block containing private key")
}
// 4. 解析PKCS#8格式私钥(兼容Java默认格式)
priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
// 尝试兼容PKCS#1格式(Java有时也会生成)
priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("解析私钥失败: %v", err)
}
}
// 5. 确认是RSA私钥
switch priv := priv.(type) {
case *rsa.PrivateKey:
return priv, nil
default:
return nil, fmt.Errorf("不是RSA私钥")
}
}
// 以下为其他辅助函数(与之前相同,保持兼容)
func generateKeyPair() (publicKey, privateKey string, err error) {
privateKeyObj, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return "", "", err
}
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKeyObj)
if err != nil {
return "", "", err
}
privateKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: privateKeyBytes,
})
publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKeyObj.PublicKey)
if err != nil {
return "", "", err
}
publicKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
})
return string(publicKeyPEM), string(privateKeyPEM), nil
}
func loadPublicKey(pubKeyStr string) (*rsa.PublicKey, error) {
pubKeyStr = strings.TrimSpace(pubKeyStr)
// 自动补全公钥PEM标签
if !strings.HasPrefix(pubKeyStr, "-----BEGIN PUBLIC KEY-----") {
pubKeyStr = "-----BEGIN PUBLIC KEY-----\n" + pubKeyStr
}
if !strings.HasSuffix(pubKeyStr, "-----END PUBLIC KEY-----") {
pubKeyStr = pubKeyStr + "\n-----END PUBLIC KEY-----"
}
block, _ := pem.Decode([]byte(pubKeyStr))
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block containing public key")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
switch pub := pub.(type) {
case *rsa.PublicKey:
return pub, nil
default:
return nil, fmt.Errorf("not an RSA public key")
}
}
// 分段加密函数 - 解决消息过长问题
func encrypt(data string, pubKey *rsa.PublicKey) (string, error) {
// RSA加密最大长度计算 (密钥长度/8 - 11填充字节)
maxLen := pubKey.Size() - 11
srcData := []byte(data)
// 如果数据长度小于等于最大长度,直接加密
if len(srcData) <= maxLen {
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, srcData)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// 否则进行分段加密
var encryptedData []string
for i := 0; i < len(srcData); i += maxLen {
end := i + maxLen
if end > len(srcData) {
end = len(srcData)
}
chunk := srcData[i:end]
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, chunk)
if err != nil {
return "", err
}
encryptedData = append(encryptedData, base64.StdEncoding.EncodeToString(ciphertext))
}
// 用"|"连接各段加密数据
return strings.Join(encryptedData, "|"), nil
}
func decrypt(ciphertext string, privKey *rsa.PrivateKey) (string, error) {
// 检查是否为分段加密数据
if strings.Contains(ciphertext, "|") {
// 分段解密
chunks := strings.Split(ciphertext, "|")
var decryptedData []byte
for _, chunk := range chunks {
data, err := base64.StdEncoding.DecodeString(chunk)
if err != nil {
return "", err
}
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, data)
if err != nil {
return "", err
}
decryptedData = append(decryptedData, plaintext...)
}
return string(decryptedData), nil
}
// 单段解密
data, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", err
}
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, data)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// 生成签名
// 签名内容:encryptedData + randomNum + timestamp
func sign(encryptedText string, randomNum string, timestamp int64, privateKey *rsa.PrivateKey) (string, error) {
if privateKey == nil {
return "", errors.New("私钥未初始化")
}
// 拼接数据
dataToSign := encryptedText + randomNum + strconv.FormatInt(timestamp, 10)
hashed := sha256.Sum256([]byte(dataToSign))
signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hashed[:])
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signature), nil
}
const (
TIMESTAMP_MAX_OFFSET = 300000 // 假设与Java版本相同的5分钟偏移量(单位毫秒)
)
func verify(encryptedText string, randomNum string, timestamp int64, signatureStr string, publicKey *rsa.PublicKey) (bool, error) {
// 1. 验证时间戳是否在允许的偏移范围内
currentTime := time.Now().UnixNano() / int64(time.Millisecond)
if math.Abs(float64(timestamp-currentTime)) > TIMESTAMP_MAX_OFFSET {
return false, nil
}
// 2. 验证签名
dataToVerify := encryptedText + randomNum + strconv.FormatInt(timestamp, 10)
hashed := sha256.Sum256([]byte(dataToVerify))
signature, err := base64.StdEncoding.DecodeString(signatureStr)
if err != nil {
return false, err
}
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signature)
if err != nil {
if err == rsa.ErrVerification {
return false, nil
}
return false, err
}
return true, nil
}
// 生成指定长度的随机数字字符串
func generateRandomNumbers() (string, int64) {
// 1. 生成10位随机数字字符串
const digits = "0123456789"
//把digits 做成随机生成的
randomNum := make([]byte, 10)
for i := range randomNum {
randomNum[i] = digits[mrand.Intn(len(digits))]
}
// 2. 获取当前时间戳(毫秒)
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
return string(randomNum), timestamp
}
func main() {
//makeKeyPair()
//EncryptData()
//DecryptData()
}
func makeKeyPair() {
// 生成密钥对
publicKey, privateKey, err := generateKeyPair()
if err != nil {
fmt.Printf("生成密钥对失败: %v\n", err)
return
}
fmt.Println("公钥:")
fmt.Println(publicKey)
fmt.Println("私钥:")
fmt.Println(privateKey)
}
func EncryptData(originalData string) (signature, encryptedText, randomNum string, timestamp int64, err error) {
/*rsaPath, err := static.RsakeyPath("go_private.key")
if err != nil {
fmt.Printf("读取私钥文件失败: %v\n", err)
return
}
// 从.key文件读取go私钥
keyContent, err := readKeyFromFile(rsaPath)
if err != nil {
fmt.Printf("读取私钥文件失败: %v\n", err)
return
}
privKey, err := loadPrivateKey(keyContent)
if err != nil {
fmt.Printf("加载私钥失败: %v\n", err)
return
}
fmt.Println("====================私钥加载成功========================")
rsaPath, err = static.RsakeyPath("public_java.key")
if err != nil {
fmt.Printf("读取公钥文件失败: %v\n", err)
return "", "", "", 0, nil
}
// public.key文件读取java公钥
keyContent1, err := readKeyFromFile(rsaPath)
if err != nil {
fmt.Printf("读取公钥文件失败: %v\n", err)
return
}
pubKey, err := loadPublicKey(keyContent1)
if err != nil {
fmt.Printf("加载公钥失败: %v\n", err)
return
}
fmt.Println("====================公钥加载成功========================")*/
// 从FTP读取私钥
privateKeyContent, err := readRSAKeyFromFTP("go_private.key")
if err != nil {
return
}
privKey, err := loadPrivateKey(privateKeyContent)
if err != nil {
return
}
// 从FTP读取公钥
publicKeyContent, err := readRSAKeyFromFTP("public_java.key")
if err != nil {
return
}
pubKey, err := loadPublicKey(publicKeyContent)
if err != nil {
return
}
// 生成10位随机数
randomNum, timestamp = generateRandomNumbers()
fmt.Println("随机数: " + randomNum)
// 生成时间戳
timestamp = time.Now().UnixMilli()
fmt.Println("时间戳: ", timestamp)
encryptedText, err = encrypt(originalData, pubKey)
if err != nil {
fmt.Printf("加密失败: %v\n", err)
return
}
fmt.Println("加密后:", encryptedText)
// 生成签名(实际使用时需要传入正确加载的privateKey)
if privKey != nil {
signature, err = sign(encryptedText, randomNum, timestamp, privKey)
if err != nil {
fmt.Println("签名错误:", err)
return
}
fmt.Println("签名: " + signature)
}
return signature, encryptedText, randomNum, timestamp, nil
}
func DecryptData(signature, encryptedText, randomNum string, timestamp int64) (decryptData string, err error) {
// 从key文件读取私钥
/*rsaPath, err := static.RsakeyPath("go_private.key")
if err != nil {
fmt.Printf("读取私钥文件失败: %v\n", err)
return "", err
}
keyContent, err := readKeyFromFile(rsaPath)
if err != nil {
fmt.Printf("读取私钥文件失败: %v\n", err)
return decryptData, err
}
privKey, err := loadPrivateKey(keyContent)
if err != nil {
fmt.Printf("加载私钥失败: %v\n", err)
return "", err
}
fmt.Println("私钥加载成功")
// public.key文件读取公钥
rsaPath, err = static.RsakeyPath("public_java.key")
keyContent1, err := readKeyFromFile(rsaPath)
if err != nil {
fmt.Printf("读取公钥文件失败: %v\n", err)
return "", err
}
pubKey, err := loadPublicKey(keyContent1)
if err != nil {
fmt.Printf("加载公钥失败: %v\n", err)
return "", err
}
fmt.Println("公钥加载成功")*/
// 从FTP读取私钥
privateKeyContent, err := readRSAKeyFromFTP("go_private.key")
if err != nil {
return
}
privKey, err := loadPrivateKey(privateKeyContent)
if err != nil {
return
}
// 从FTP读取公钥
publicKeyContent, err := readRSAKeyFromFTP("public_java.key")
if err != nil {
return
}
pubKey, err := loadPublicKey(publicKeyContent)
if err != nil {
return
}
// 验证签名(实际使用时需要传入正确加载的publicKey)
if pubKey != nil {
valid, err := verify(encryptedText, randomNum, int64(timestamp), signature, pubKey)
if err != nil {
fmt.Println("验证错误:", err)
} else {
fmt.Println("签名验证结果: ", valid)
}
} else {
fmt.Println("请先加载公钥")
}
decryptData, err = decrypt(encryptedText, privKey)
if err != nil {
fmt.Printf("解密失败: %v\n", err)
return decryptData, nil
}
fmt.Println("解密后:", decryptData)
return decryptData, nil
}
func readRSAKeyFromFTP(filename string) (string, error) {
// 获取FTP配置
ftpConn, err := file_store.Ftp()
if err != nil {
return "", err
}
defer ftpConn.Quit()
// 构建完整的FTP路径
basePath := client.ReadConfig("ftp.ftp_base_path")
remotePath := basePath + "rsa_key/" + filename
// 读取文件
response, err := ftpConn.Retr(remotePath)
if err != nil {
return "", fmt.Errorf("FTP读取失败: %v", err)
}
defer response.Close()
data, err := io.ReadAll(response)
if err != nil {
return "", fmt.Errorf("读取数据失败: %v", err)
}
return string(data), nil
}