hb-20260724155322/hb/crypto.go

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