插件-支付宝红包转账,签名,证书加载,加密处理,验签

This commit is contained in:
李子铭 2024-08-20 18:37:25 +08:00
parent b40c2ad29e
commit 9091b6b74c
8 changed files with 82 additions and 15 deletions

View File

@ -83,7 +83,7 @@ func (s *AlipayCpnService) Notify(_ context.Context, request *proto.NotifyReques
} }
n := notifyReq(request) n := notifyReq(request)
b, err := Verify(n, c.Npk) b, err := Verify(n, c.PukPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -15,7 +15,7 @@ func config() []byte {
c := &Config{ c := &Config{
AppId: "2021004100663111", AppId: "2021004100663111",
Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbA+YuMp4JUVj6rjzgwGKNXWkEMGX/rinqkfyBZ6B6p8EKz8zgA+ypiJLOixD3GyKnUnAzx4waNRZHfdEu+l57kJFtd/ipfwtJ28aTi7TqtqEpqD+UPY4ourt2CuyCFxWsonS6dczqtTvfAVArTdbGJYY+kNNVR3WiXgGUhkUu8N7vEowU00RUQGNdSVMUs4FX+HlU3RnEoRc/xUhPiaLf0Bm/g9wG96kwyg/TZvkNU7PpMVRdXeLrVORn0qThs3VA4dqondF+O12iC1TK4TKYGzFYczGAUsfuurtDyCc2GMoE+hH2FR8U7amQOVuYZFkutTdqaqukWpFQOr8wLeMzAgMBAAECggEAD715/3v3y6ejA3EeQvDQpGRANeLckcGMlaUkRpCSAf6oawSALuFZUt3T3zAzae7zUJ8mHTKMKR4DmeO68utfevXq3bkvj87nmslGvjfeKrgxYPMMjrTV0KuK6XLjiH3sOtn6FaR9s6iOwvovLs2LT/ZGbZyu84QNOjwTVP9JXZQkBgMItdKf+U3H2Cjp7U/qXBt8/9yzVFklp1g1883DAty0lzmT27dJimGVGaPQ8vNxo81+ZUEJAn6GUTk0K/GwJfhPTU8hh8G90n2LTyskoMjGxQe9lXfCcS9DmWawEQL4WTctPrDYlnS/cjCVMS0KXIFuxRNf6qaMYDeywC8BgQKBgQDyf40dvmw34Rrb46+NLayQ9W5CJI/dYeRajpCjoOomq5QYhbXzUCpVfbtByeGMsg3zN58NNsZGhl5SU0GdEdoOlxCk+2Hey2yQYF4ugQm/dTd68Jgqi3yujigAbNYa1ZhL9t3FqouPY1dGiaxl+DFYdSMIrsVFXh2NbrPyqTk5IQKBgQDnNaH7LCcIUqg9H8Tsls+8GLzP2HwF3hdll8asEsF3K3HX6/Zlp4VnnEcIkAxLRL/L0o5akXrmA18ZwfoSguTPXV9va3G2GgIiJHgcytmGtQVvbpFKuPnCXKz+avxnfO0flJqyYEuHr/40jsGbMkk0Kr52/n3ivXZbBUlT+tkt0wKBgB0qLgSnxEgsMJjFl3V5SsncWrhlwU+02Evz3X1wevjPpe4VFr7+ozjI+F5/MztCpt7bj6t9LPeKbYmlLb0ASqN6k6vj9+9ds97hWDJrnoqCRHvqt8JWKFauDi2O6WksyzZHqIB/dG14WyTGpg9VfEnRPLdsnZksKo26BLZol9NBAoGBAMz7oMNljqlzVtLyMo2q2zuhFuySusoc79NTL4FpE3rK2qCbA5V2YvDL/bIau7uTlRNodmrXZgU84fidIE9/Gsq5tp26vVK8Vj3c5Vxpf1dNcCct+MQtoMjvjzP0uBgsCrKf9lLEytHed1ozYnRsrbgBWWF4GTWH0cG6uxsoX5mfAoGAfYXKZdyRU+Su4y4EnDLMXd320ar7PaeuY8aZU7V6UQEsaOj6H3O8JMEOuBrOVEhAP2EkAC8ayargSXTSOkN97pg88agKDwA6jh4N6TKAK8XMft81YPPliVwZMsAUqihSKQBnKZ7ssHHLGWWWp5vfkyb7Y7dIkZcPzB0X3q/jL58=", Prk: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbA+YuMp4JUVj6rjzgwGKNXWkEMGX/rinqkfyBZ6B6p8EKz8zgA+ypiJLOixD3GyKnUnAzx4waNRZHfdEu+l57kJFtd/ipfwtJ28aTi7TqtqEpqD+UPY4ourt2CuyCFxWsonS6dczqtTvfAVArTdbGJYY+kNNVR3WiXgGUhkUu8N7vEowU00RUQGNdSVMUs4FX+HlU3RnEoRc/xUhPiaLf0Bm/g9wG96kwyg/TZvkNU7PpMVRdXeLrVORn0qThs3VA4dqondF+O12iC1TK4TKYGzFYczGAUsfuurtDyCc2GMoE+hH2FR8U7amQOVuYZFkutTdqaqukWpFQOr8wLeMzAgMBAAECggEAD715/3v3y6ejA3EeQvDQpGRANeLckcGMlaUkRpCSAf6oawSALuFZUt3T3zAzae7zUJ8mHTKMKR4DmeO68utfevXq3bkvj87nmslGvjfeKrgxYPMMjrTV0KuK6XLjiH3sOtn6FaR9s6iOwvovLs2LT/ZGbZyu84QNOjwTVP9JXZQkBgMItdKf+U3H2Cjp7U/qXBt8/9yzVFklp1g1883DAty0lzmT27dJimGVGaPQ8vNxo81+ZUEJAn6GUTk0K/GwJfhPTU8hh8G90n2LTyskoMjGxQe9lXfCcS9DmWawEQL4WTctPrDYlnS/cjCVMS0KXIFuxRNf6qaMYDeywC8BgQKBgQDyf40dvmw34Rrb46+NLayQ9W5CJI/dYeRajpCjoOomq5QYhbXzUCpVfbtByeGMsg3zN58NNsZGhl5SU0GdEdoOlxCk+2Hey2yQYF4ugQm/dTd68Jgqi3yujigAbNYa1ZhL9t3FqouPY1dGiaxl+DFYdSMIrsVFXh2NbrPyqTk5IQKBgQDnNaH7LCcIUqg9H8Tsls+8GLzP2HwF3hdll8asEsF3K3HX6/Zlp4VnnEcIkAxLRL/L0o5akXrmA18ZwfoSguTPXV9va3G2GgIiJHgcytmGtQVvbpFKuPnCXKz+avxnfO0flJqyYEuHr/40jsGbMkk0Kr52/n3ivXZbBUlT+tkt0wKBgB0qLgSnxEgsMJjFl3V5SsncWrhlwU+02Evz3X1wevjPpe4VFr7+ozjI+F5/MztCpt7bj6t9LPeKbYmlLb0ASqN6k6vj9+9ds97hWDJrnoqCRHvqt8JWKFauDi2O6WksyzZHqIB/dG14WyTGpg9VfEnRPLdsnZksKo26BLZol9NBAoGBAMz7oMNljqlzVtLyMo2q2zuhFuySusoc79NTL4FpE3rK2qCbA5V2YvDL/bIau7uTlRNodmrXZgU84fidIE9/Gsq5tp26vVK8Vj3c5Vxpf1dNcCct+MQtoMjvjzP0uBgsCrKf9lLEytHed1ozYnRsrbgBWWF4GTWH0cG6uxsoX5mfAoGAfYXKZdyRU+Su4y4EnDLMXd320ar7PaeuY8aZU7V6UQEsaOj6H3O8JMEOuBrOVEhAP2EkAC8ayargSXTSOkN97pg88agKDwA6jh4N6TKAK8XMft81YPPliVwZMsAUqihSKQBnKZ7ssHHLGWWWp5vfkyb7Y7dIkZcPzB0X3q/jL58=",
Npk: "", PukPath: "",
MchCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt", MchCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt",
RootCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt", RootCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt",
} }

View File

@ -15,7 +15,7 @@ type Config struct {
AppId string `json:"app_id"` AppId string `json:"app_id"`
BaseUri string `json:"base_uri"` BaseUri string `json:"base_uri"`
Prk string `json:"prk"` // 私钥 Prk string `json:"prk"` // 私钥
Npk string `json:"npk"` // 回调公钥 PukPath string `json:"npk"` // 验签公钥
MchCertPath string `json:"mch_cert_path"` MchCertPath string `json:"mch_cert_path"`
RootCertPath string `json:"root_cert_path"` RootCertPath string `json:"root_cert_path"`
@ -37,7 +37,7 @@ func (c *Config) paramReq(req po.Req, method string) (*po.Param, error) {
if err := req.Validate(); err != nil { if err := req.Validate(); err != nil {
return nil, err return nil, err
} }
cert, err := alipay.GetCert(c.MchCertPath, c.RootCertPath, c.AppId) cert, err := alipay.GetCert(c.MchCertPath, c.RootCertPath, c.PukPath, c.AppId)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -8,6 +8,7 @@ import (
type CertConfig struct { type CertConfig struct {
MchCertSN string MchCertSN string
RootCertSN string RootCertSN string
PublicKey string
} }
type manager struct { type manager struct {
@ -36,7 +37,7 @@ func init() {
instance.CertConfigs = make(map[string]*CertConfig) instance.CertConfigs = make(map[string]*CertConfig)
} }
func GetCert(mchCertPath, rootCertPath, appId string) (*CertConfig, error) { func GetCert(mchCertPath, rootCertPath, PublicKeyPath, appId string) (*CertConfig, error) {
if mchCertPath == "" || rootCertPath == "" || appId == "" { if mchCertPath == "" || rootCertPath == "" || appId == "" {
return nil, fmt.Errorf("mchCertPath or rootCertPath or appId is empty") return nil, fmt.Errorf("mchCertPath or rootCertPath or appId is empty")
} }
@ -44,17 +45,22 @@ func GetCert(mchCertPath, rootCertPath, appId string) (*CertConfig, error) {
if c != nil { if c != nil {
return nil, fmt.Errorf("appId %s already exists", appId) return nil, fmt.Errorf("appId %s already exists", appId)
} }
mchCertSN, err := GetMchCertSN(mchCertPath) mchCertSN, err := getMchCertSN(mchCertPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("get mchCertSN error: %v", err) return nil, fmt.Errorf("get mchCertSN error: %v", err)
} }
rootCertSN, err := GetRootCertSN(rootCertPath) rootCertSN, err := getRootCertSN(rootCertPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("get rootCertSN error: %v", err) return nil, fmt.Errorf("get rootCertSN error: %v", err)
} }
publicKey, err := getPublicKey(PublicKeyPath)
if err != nil {
return nil, fmt.Errorf("get publicKey error: %v", err)
}
c = &CertConfig{ c = &CertConfig{
MchCertSN: mchCertSN, MchCertSN: mchCertSN,
RootCertSN: rootCertSN, RootCertSN: rootCertSN,
PublicKey: publicKey,
} }
setCertConfig(appId, c) setCertConfig(appId, c)
return c, nil return c, nil

View File

@ -5,7 +5,7 @@ import (
) )
func TestCertSN(t *testing.T) { func TestCertSN(t *testing.T) {
ms, err := GetMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt") ms, err := getMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else { } else {
@ -13,7 +13,7 @@ func TestCertSN(t *testing.T) {
//78a57140055b8e7853b1576bcf763361 //78a57140055b8e7853b1576bcf763361
t.Logf("merchantCertSN%s", ms) t.Logf("merchantCertSN%s", ms)
} }
rs, err := GetRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt") rs, err := getRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else { } else {
@ -24,7 +24,7 @@ func TestCertSN(t *testing.T) {
} }
func TestMchCertSN(t *testing.T) { func TestMchCertSN(t *testing.T) {
s, err := GetMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt") s, err := getMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else { } else {
@ -34,10 +34,20 @@ func TestMchCertSN(t *testing.T) {
} }
func TestRootCertSN(t *testing.T) { func TestRootCertSN(t *testing.T) {
s, err := GetRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt") s, err := getRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else { } else {
t.Log(s) t.Log(s)
} }
} }
func TestPublicKey(t *testing.T) {
s, err := getPublicKey("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayCertPublicKey_RSA2.crt")
if err != nil {
t.Error(err)
} else {
//78a57140055b8e7853b1576bcf763361
t.Log(s)
}
}

View File

@ -6,8 +6,8 @@ import (
"log" "log"
) )
// GetMchCertSN 提取证书序列号 // getMchCertSN 提取证书序列号
func GetMchCertSN(certPath string) (string, error) { func getMchCertSN(certPath string) (string, error) {
certData, err := ioutil.ReadFile(certPath) certData, err := ioutil.ReadFile(certPath)
if err != nil { if err != nil {
log.Fatalf("Failed to read the cert file: %s", err) log.Fatalf("Failed to read the cert file: %s", err)

View File

@ -0,0 +1,51 @@
package alipay
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"strings"
)
// 从证书文件中提取公钥
func getPublicKey(certPath string) (string, error) {
// 读取证书文件内容
certData, err := ioutil.ReadFile(certPath)
if err != nil {
return "", fmt.Errorf("failed to read certificate file: %w", err)
}
// 解码 PEM 编码的证书
block, _ := pem.Decode(certData)
if block == nil {
return "", fmt.Errorf("failed to decode PEM block")
}
// 解析证书
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("failed to parse certificate: %w", err)
}
// 获取公钥
pubKey := cert.PublicKey
// 转换公钥为 PEM 格式
pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", fmt.Errorf("failed to marshal public key: %w", err)
}
// 创建 PEM 块
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: pubKeyBytes,
}
// 编码为 PEM 格式字符串
pemBytes := pem.EncodeToMemory(pemBlock)
publicKey := strings.TrimSpace(string(pemBytes))
return publicKey, nil
}

View File

@ -7,8 +7,8 @@ import (
"strings" "strings"
) )
// GetRootCertSN 计算证书的序列号并返回最终字符串 // getRootCertSN 计算证书的序列号并返回最终字符串
func GetRootCertSN(certPath string) (string, error) { func getRootCertSN(certPath string) (string, error) {
certData, err := ioutil.ReadFile(certPath) certData, err := ioutil.ReadFile(certPath)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to read certificate file: %v", err) return "", fmt.Errorf("failed to read certificate file: %v", err)