当前位置: 首页 > news >正文

golang实现的Rsa加密解密算法(go和java交互时双向加解密方案)

加粗样式golang实现的Rsa加密解密算法(go和java交互时双向加解密方案)

使用说明:

  1. public_java.key 是java生成的公钥,需要java方生成并发给go方保存。
  2. go_private.key和go_public.key 是go生成的公私钥。其中go_public.key文件需发给java方保存,java方用来验证go给的签名,同时java方加密内容时也会用到go_public.key。反之java方也是同样的道理
  3. 支持分段加密, 可以 解决消息过长问题
package utilsimport ("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. 解码PEMblock, 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, nildefault: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, nildefault:return nil, fmt.Errorf("not an RSA public key")}
}// 分段加密函数 - 解决消息过长问题
func encrypt(data string, pubKey *rsa.PublicKey) (string, error) {// RSA加密最大长度计算 (密钥长度/8 - 11填充字节)maxLen := pubKey.Size() - 11srcData := []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 []stringfor i := 0; i < len(srcData); i += maxLen {end := i + maxLenif 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 []bytefor _, 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
}
http://www.dtcms.com/a/342540.html

相关文章:

  • OpenCV 形态学操作详解:腐蚀、膨胀与开闭运算
  • Chrome/360 浏览器 WebUI 资源底层机制解析:共享资源与专属资源的奥秘
  • 一、部署LNMP
  • mac的m3芯片安装JDK8、JDK17
  • 【CDA干货】金融App产品AB测试与指标体系搭建
  • 数据结构之排序大全(3)
  • 31、工业网络异常行为检测与OT协议深度分析 (核电站DCS模拟) - /安全与维护组件/network-anomaly-detection-nuclear
  • “码” 上安全:轻量级加密的硬件实现与侧信道攻击防御
  • Java实现一个简单的LRU缓存对象
  • 【SpringBoot】16 核心功能 - Web开发原理 - 请求参数 - 源码分析
  • WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析七
  • 如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
  • CTFshow系列——命令执行web41-44
  • YOLOv8 原理与跨领域应用全景分析
  • CVPR | 2025 | MAP:通过掩码自回归预训练释放混合 Mamba - Transformer 视觉骨干网络的潜力
  • 【C++】仿函数和回调函数
  • Python数值取整完全指南:从基础到金融工程实践
  • uniapp实现分页,效果如图
  • 自然语言处理——04 注意力机制
  • npm全局安装后,cmd命令行可以访问,vscode访问报错
  • HTTP 403 错误:后端权限校验机制深度解析
  • 长尾关键词优化SEO核心策略
  • JeeSite 快速开发平台:全能企业级快速开发解决方案
  • 自己动手,在Mac开发机上利用ollama部署一款轻量级的大模型Phi-3:mini
  • ElasticSearch——常用命令
  • VSCode Import Cost:5 分钟学会依赖瘦身
  • java16学习笔记
  • uniapp 全局弹窗
  • 力扣1005:k次取反后最大化的数组和
  • pycharm编译器如何快速掌握一个新模块的使用方法