155 lines
4.0 KiB
Go
155 lines
4.0 KiB
Go
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
|
||
} |