diff --git a/hb/crypto.go b/hb/crypto.go new file mode 100644 index 0000000..9b67be9 --- /dev/null +++ b/hb/crypto.go @@ -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 +} \ No newline at end of file