Files
2026-03-13 15:51:59 +08:00

143 lines
3.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package heepay
import (
"bytes"
"crypto/des"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
)
// EncryptRequest 使用 RSA+3DES 加密请求体
// 1. 生成随机 3DES 密钥24 字节)
// 2. 用 3DES-CBC 加密 JSON 请求体
// 3. 用汇元 RSA 公钥加密 3DES 密钥
func EncryptRequest(plaintext []byte, publicKeyPEM string) (encData string, encKey string, err error) {
pubKey, err := parseRSAPublicKey(publicKeyPEM)
if err != nil {
return "", "", err
}
// 生成 3DES 密钥
desKey := make([]byte, 24)
if _, err = rand.Read(desKey); err != nil {
return "", "", err
}
// 3DES 加密
ciphertext, err := tripleDesEncrypt(plaintext, desKey)
if err != nil {
return "", "", err
}
// RSA 加密 3DES 密钥
encKeyBytes, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, desKey)
if err != nil {
return "", "", err
}
encData = base64.StdEncoding.EncodeToString(ciphertext)
encKey = base64.StdEncoding.EncodeToString(encKeyBytes)
return
}
// DecryptResponse 使用 RSA 私钥 + 3DES 解密响应
func DecryptResponse(encData, encKey, privateKeyPEM string) ([]byte, error) {
privKey, err := parseRSAPrivateKey(privateKeyPEM)
if err != nil {
return nil, err
}
encKeyBytes, err := base64.StdEncoding.DecodeString(encKey)
if err != nil {
return nil, err
}
desKey, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, encKeyBytes)
if err != nil {
return nil, err
}
ciphertext, err := base64.StdEncoding.DecodeString(encData)
if err != nil {
return nil, err
}
return tripleDesDecrypt(ciphertext, desKey)
}
// tripleDesEncrypt 3DES-CBC 加密PKCS5Padding
func tripleDesEncrypt(plaintext, key []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
plaintext = pkcs5Padding(plaintext, blockSize)
iv := key[:blockSize] // 使用密钥前 8 字节作为 IV
mode := newCBCEncrypter(block, iv)
ciphertext := make([]byte, len(plaintext))
mode.CryptBlocks(ciphertext, plaintext)
return ciphertext, nil
}
// tripleDesDecrypt 3DES-CBC 解密
func tripleDesDecrypt(ciphertext, key []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
iv := key[:blockSize]
mode := newCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
return pkcs5Unpadding(plaintext)
}
func pkcs5Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
func pkcs5Unpadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("empty data")
}
padding := int(data[length-1])
if padding > length {
return nil, errors.New("invalid padding")
}
return data[:length-padding], nil
}
func parseRSAPublicKey(pemStr string) (*rsa.PublicKey, error) {
block, _ := pem.Decode([]byte(pemStr))
if block == nil {
return nil, errors.New("failed to decode PEM block")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
return nil, errors.New("not RSA public key")
}
return rsaPub, nil
}
func parseRSAPrivateKey(pemStr string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(pemStr))
if block == nil {
return nil, errors.New("failed to decode PEM block")
}
return x509.ParsePKCS1PrivateKey(block.Bytes)
}