143 lines
3.5 KiB
Go
143 lines
3.5 KiB
Go
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)
|
||
}
|