添加文件: hb/crypto.go

This commit is contained in:
renzhiyuan 2026-07-24 15:53:25 +08:00
parent 189ffc83fc
commit 6f5b142bfc
1 changed files with 155 additions and 0 deletions

155
hb/crypto.go Normal file
View File

@ -0,0 +1,155 @@
package hb
import (
"crypto"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"strings"
)
// buildSignString 按文档指定顺序拼接签名串(请求)
// 参数顺序merchantId, requestId, signType, type, version, orderId, amount
func buildSignString(params map[string]string, orderedKeys []string) string {
var parts []string
for _, k := range orderedKeys {
if v, ok := params[k]; ok {
parts = append(parts, fmt.Sprintf("%s=%s", k, v))
}
}
return strings.Join(parts, "&")
}
// getRequestSignKeys 获取请求签名参数顺序
func getRequestSignKeys() []string {
return []string{"merchantId", "requestId", "signType", "type", "version", "orderId", "amount"}
}
// getResponseSignKeys 获取响应签名参数顺序
func getResponseSignKeys() []string {
return []string{"merchantId", "payNo", "returnCode", "message", "signType", "type", "version", "amount", "orderId", "status"}
}
// SignMD5 MD5签名
// 在待签名数据之后加上商户密钥signKey生成MD5摘要
func SignMD5(signStr, signKey string) string {
data := signStr + signKey
hash := md5.Sum([]byte(data))
return hex.EncodeToString(hash[:])
}
// VerifyMD5 验证MD5签名
func VerifyMD5(signStr, signKey, hmac string) bool {
return SignMD5(signStr, signKey) == hmac
}
// SignRSA RSA签名配合SHA-1
// 使用商户私钥对签名串进行RSA-SHA1签名
func SignRSA(signStr string, privateKeyPEM string) (string, error) {
block, _ := pem.Decode([]byte(privateKeyPEM))
if block == nil {
return "", fmt.Errorf("failed to decode PEM private key")
}
var privateKey *rsa.PrivateKey
// 尝试PKCS8格式
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err == nil {
var ok bool
privateKey, ok = key.(*rsa.PrivateKey)
if !ok {
return "", fmt.Errorf("not a RSA private key")
}
} else {
// 尝试PKCS1格式
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", fmt.Errorf("failed to parse private key (PKCS1/PKCS8): %v", err)
}
}
// 计算SHA-1哈希
hash := sha1.Sum([]byte(signStr))
// RSA签名
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, hash[:])
if err != nil {
return "", fmt.Errorf("RSA sign failed: %v", err)
}
return hex.EncodeToString(signature), nil
}
// VerifyRSA 验证RSA签名
// 使用手机支付公钥验证响应签名
func VerifyRSA(signStr, signatureHex, publicKeyPEM string) error {
block, _ := pem.Decode([]byte(publicKeyPEM))
if block == nil {
return fmt.Errorf("failed to decode PEM public key")
}
// 尝试解析公钥
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
// 尝试解析证书
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse public key: %v", err)
}
pub = cert.PublicKey
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("not a RSA public key")
}
// 解码签名
signature, err := hex.DecodeString(signatureHex)
if err != nil {
return fmt.Errorf("failed to decode signature hex: %v", err)
}
// 计算SHA-1哈希
hash := sha1.Sum([]byte(signStr))
// 验证签名
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA1, hash[:], signature)
if err != nil {
return fmt.Errorf("RSA verify failed: %v", err)
}
return nil
}
// extractCertFromPEM 从PEM格式的证书中提取公钥PEM
// 有些场景下serverCert返回的是证书需要提取公钥
func extractPublicKeyFromCert(certPEM string) (string, error) {
block, _ := pem.Decode([]byte(certPEM))
if block == nil {
return "", fmt.Errorf("failed to decode PEM certificate")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("failed to parse certificate: %v", err)
}
pubKeyBytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
if err != nil {
return "", fmt.Errorf("failed to marshal public key: %v", err)
}
pubKeyPEM := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: pubKeyBytes,
})
return string(pubKeyPEM), nil
}