支付宝token获取
This commit is contained in:
parent
d9c3e7ad88
commit
d15e6d43be
|
@ -0,0 +1,74 @@
|
|||
package alipay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/alipay/vo"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
const BaseUri = "https://openapi.alipay.com/gateway.do"
|
||||
|
||||
const (
|
||||
Version = "1.0"
|
||||
Format = "json"
|
||||
SignType = "RSA2"
|
||||
Charset = "UTF-8"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
AppId string // 应用ID
|
||||
Prk string // 私钥
|
||||
PukPath string // 验签公钥
|
||||
MchCertPath string // 商户证书路径
|
||||
RootCertPath string // 根证书路径
|
||||
NotifyUrl string // 回调地址
|
||||
}
|
||||
|
||||
type Req interface {
|
||||
Validate() error
|
||||
}
|
||||
|
||||
var _ Req = (*Param)(nil)
|
||||
|
||||
type Param struct {
|
||||
AlipayRootCertSn string `json:"alipay_root_cert_sn"`
|
||||
AppCertSn string `json:"app_cert_sn"`
|
||||
AppId string `json:"app_id" validate:"required"`
|
||||
Method string `json:"method" validate:"required"`
|
||||
Format string `json:"format" validate:"required"`
|
||||
Charset string `json:"charset" validate:"required"`
|
||||
SignType string `json:"sign_type" validate:"required"`
|
||||
Timestamp string `json:"timestamp" validate:"required"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
BizContent string `json:"biz_content"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Code vo.Code `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
SubCode string `json:"sub_code"`
|
||||
SubMsg string `json:"sub_msg"`
|
||||
}
|
||||
|
||||
type Notify struct {
|
||||
Charset string `json:"charset"`
|
||||
UtcTimestamp string `json:"utc_timestamp"`
|
||||
AppId string `json:"app_id"`
|
||||
Version string `json:"version"`
|
||||
SignType string `json:"sign_type"`
|
||||
NotifyId string `json:"notify_id"`
|
||||
MsgMethod string `json:"msg_method"`
|
||||
Sign string `json:"sign"`
|
||||
BizContent string `json:"biz_content"`
|
||||
}
|
||||
|
||||
func (req *Param) Validate() error {
|
||||
err := validator.New().Struct(req)
|
||||
if err != nil {
|
||||
for _, err = range err.(validator.ValidationErrors) {
|
||||
return fmt.Errorf("参数有误:" + err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/alipay"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/alipay/utils"
|
||||
"github.com/carlmjohnson/requests"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Token alipay.Client
|
||||
|
||||
func (t *Token) Client(ctx context.Context, code string) (*AlipayTokenResponse, error) {
|
||||
cert, err := utils.GetCert(t.MchCertPath, t.RootCertPath, t.PukPath, t.AppId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
param := &Param{
|
||||
AlipayRootCertSn: cert.RootCertSN,
|
||||
AppCertSn: cert.MchCertSN,
|
||||
AppId: t.AppId,
|
||||
Method: "alipay.system.oauth.token",
|
||||
Format: alipay.Format,
|
||||
Charset: alipay.Charset,
|
||||
SignType: alipay.SignType,
|
||||
Timestamp: time.Now().Format(time.DateTime),
|
||||
Version: alipay.Version,
|
||||
Sign: "",
|
||||
GrantType: "authorization_code",
|
||||
Code: code,
|
||||
}
|
||||
uv, err := utils.UrlValues(t.Prk, param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response AlipayTokenResponse
|
||||
err = requests.URL(alipay.BaseUri).Post().Params(uv).ToJSON(&response).Fetch(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求异常,msg:" + err.Error())
|
||||
}
|
||||
return &response, nil
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToken_Client(t *testing.T) {
|
||||
d := Token{
|
||||
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=",
|
||||
PukPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayCertPublicKey_RSA2.crt",
|
||||
MchCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt",
|
||||
RootCertPath: "/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt",
|
||||
}
|
||||
resp, err := d.Client(context.Background(), "349feb5fb58e455da1b00a7748ceMD68")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if resp.IsSuccess() {
|
||||
t.Logf("Response: %+v", resp.Response)
|
||||
} else {
|
||||
t.Errorf("ErrorResponse: %+v", resp.ErrorResponse)
|
||||
t.Errorf("ErrorResponse Msg: %+v", resp.ErrorResponse.Msg)
|
||||
t.Errorf("ErrorResponse SubCode: %+v", resp.ErrorResponse.SubCode)
|
||||
t.Errorf("ErrorResponse SubMsg: %+v", resp.ErrorResponse.SubMsg)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/alipay"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type Param struct {
|
||||
AlipayRootCertSn string `json:"alipay_root_cert_sn"`
|
||||
AppCertSn string `json:"app_cert_sn"`
|
||||
AppId string `json:"app_id" validate:"required"`
|
||||
Method string `json:"method" validate:"required"`
|
||||
Format string `json:"format" validate:"required"`
|
||||
Charset string `json:"charset" validate:"required"`
|
||||
SignType string `json:"sign_type" validate:"required"`
|
||||
Timestamp string `json:"timestamp" validate:"required"`
|
||||
Version string `json:"version" validate:"required"`
|
||||
BizContent string `json:"biz_content"`
|
||||
Sign string `json:"sign"`
|
||||
|
||||
GrantType string `json:"grant_type"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
type AlipayTokenSuccessResponse struct {
|
||||
UserId string `json:"user_id"`
|
||||
OpenId string `json:"open_id"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn string `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ReExpiresIn string `json:"re_expires_in"`
|
||||
AuthStart string `json:"auth_start"`
|
||||
}
|
||||
|
||||
type AlipayTokenResponse struct {
|
||||
ErrorResponse *alipay.ErrorResponse `json:"error_response"`
|
||||
Response *AlipayTokenSuccessResponse `json:"alipay_system_oauth_token_response"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
func (req *Param) Validate() error {
|
||||
err := validator.New().Struct(req)
|
||||
if err != nil {
|
||||
for _, err = range err.(validator.ValidationErrors) {
|
||||
return fmt.Errorf("参数有误:" + err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AlipayTokenResponse) IsSuccess() bool {
|
||||
return r.ErrorResponse == nil
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type CertConfig struct {
|
||||
MchCertSN string
|
||||
RootCertSN string
|
||||
PublicKey string
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
once sync.Once
|
||||
mutex sync.RWMutex
|
||||
CertConfigs map[string]*CertConfig
|
||||
}
|
||||
|
||||
var instance manager
|
||||
|
||||
func getCertConfig(appId string) *CertConfig {
|
||||
c, ok := instance.CertConfigs[appId]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func setCertConfig(appId string, certConfig *CertConfig) {
|
||||
instance.mutex.Lock()
|
||||
defer instance.mutex.Unlock()
|
||||
instance.CertConfigs[appId] = certConfig
|
||||
}
|
||||
|
||||
func init() {
|
||||
instance.CertConfigs = make(map[string]*CertConfig)
|
||||
}
|
||||
|
||||
func GetCert(mchCertPath, rootCertPath, PublicKeyPath, appId string) (*CertConfig, error) {
|
||||
if mchCertPath == "" || rootCertPath == "" || appId == "" {
|
||||
return nil, fmt.Errorf("mchCertPath or rootCertPath or appId is empty")
|
||||
}
|
||||
c := getCertConfig(appId)
|
||||
if c != nil {
|
||||
return nil, fmt.Errorf("appId %s already exists", appId)
|
||||
}
|
||||
mchCertSN, err := getMchCertSN(mchCertPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get mchCertSN error: %v", err)
|
||||
}
|
||||
rootCertSN, err := getRootCertSN(rootCertPath)
|
||||
if err != nil {
|
||||
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{
|
||||
MchCertSN: mchCertSN,
|
||||
RootCertSN: rootCertSN,
|
||||
PublicKey: publicKey,
|
||||
}
|
||||
setCertConfig(appId, c)
|
||||
return c, nil
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCertSN(t *testing.T) {
|
||||
ms, err := getMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
//78a57140055b8e7853b1576bcf763361
|
||||
//78a57140055b8e7853b1576bcf763361
|
||||
t.Logf("merchantCertSN:%s", ms)
|
||||
}
|
||||
rs, err := getRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
//687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6
|
||||
//687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6
|
||||
t.Logf("alipayRootCertSN:%s", rs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMchCertSN(t *testing.T) {
|
||||
s, err := getMchCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/appCertPublicKey_2021004100663111.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
//78a57140055b8e7853b1576bcf763361
|
||||
t.Log(s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootCertSN(t *testing.T) {
|
||||
s, err := getRootCertSN("/Users/lsxd/code/php/yxxt/market/config/alipaycash/alipayRootCert.crt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// md5Hash 计算 MD5 哈希值
|
||||
func md5Hash(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// hex2dec 将字节数组转换为十六进制字符串
|
||||
func hex2dec(hex string) string {
|
||||
var dec int
|
||||
fmt.Sscanf(hex, "%x", &dec)
|
||||
return fmt.Sprintf("%d", dec)
|
||||
}
|
||||
|
||||
func getCert(certData []byte) (*x509.Certificate, error) {
|
||||
block, _ := pem.Decode(certData)
|
||||
if block == nil {
|
||||
log.Fatal("Failed to parse PEM block containing the cert")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
// getMchCertSN 提取证书序列号
|
||||
func getMchCertSN(certPath string) (string, error) {
|
||||
certData, err := ioutil.ReadFile(certPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read the cert file: %s", err)
|
||||
}
|
||||
|
||||
cert, err := getCert(certData)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
return md5Hash(cert.Issuer.ToRDNSequence().String() + cert.SerialNumber.String()), nil
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package utils
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// getRootCertSN 计算证书的序列号并返回最终字符串
|
||||
func getRootCertSN(certPath string) (string, error) {
|
||||
certData, err := ioutil.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read certificate file: %v", err)
|
||||
}
|
||||
var sn string
|
||||
blocks := strings.Split(string(certData), "-----END CERTIFICATE-----")
|
||||
for _, blockStr := range blocks[:len(blocks)-1] {
|
||||
cert, err := getCert([]byte(strings.TrimSpace(blockStr) + "\n-----END CERTIFICATE-----"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
serialNumber := cert.SerialNumber.String()
|
||||
if strings.HasPrefix(serialNumber, "0x") {
|
||||
serialNumber = hex2dec(serialNumber[2:])
|
||||
}
|
||||
if cert.SignatureAlgorithm == x509.SHA1WithRSA || cert.SignatureAlgorithm == x509.SHA256WithRSA {
|
||||
hash := md5Hash(cert.Issuer.ToRDNSequence().String() + serialNumber)
|
||||
if sn == "" {
|
||||
sn = hash
|
||||
} else {
|
||||
sn += "_" + hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sn, nil
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/alipay"
|
||||
"gitea.cdlsxd.cn/BaseSystem/plugin/utils"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func UrlValues(prk string, req alipay.Req) (url.Values, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var strToBeSigned strings.Builder
|
||||
uv := url.Values{}
|
||||
kvRows := utils.SortStructJsonTag(req)
|
||||
for _, kv := range kvRows {
|
||||
if kv.Key == "sign" || kv.Value == "" {
|
||||
continue
|
||||
}
|
||||
uv.Set(kv.Key, kv.Value)
|
||||
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
|
||||
}
|
||||
s := strings.TrimRight(strToBeSigned.String(), "&")
|
||||
sign, err := Sign(s, []byte(utils.NewPrivate().Build(prk)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uv.Set("sign", sign)
|
||||
|
||||
return uv, nil
|
||||
}
|
||||
|
||||
func Sign(data string, privateKeyPEM []byte) (string, error) {
|
||||
block, _ := pem.Decode(privateKeyPEM)
|
||||
if block == nil {
|
||||
return "", errors.New("failed to parse PEM block containing the private key")
|
||||
}
|
||||
|
||||
privyKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse DER encoded private key: %v", err)
|
||||
}
|
||||
|
||||
hashed := sha256.Sum256([]byte(data))
|
||||
signature, err := rsa.SignPKCS1v15(rand.Reader, privyKey.(*rsa.PrivateKey), crypto.SHA256, hashed[:])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sign: %v", err)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
|
||||
func Verify(n *alipay.Notify, publicKeyPEM string) (bool, error) {
|
||||
var strToBeSigned strings.Builder
|
||||
uv := url.Values{}
|
||||
kvRows := utils.SortStructJsonTag(n)
|
||||
for _, kv := range kvRows {
|
||||
if kv.Key == "sign" || kv.Key == "sign_type" || kv.Value == "" {
|
||||
continue
|
||||
}
|
||||
uv.Set(kv.Key, kv.Value)
|
||||
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
|
||||
}
|
||||
s := strings.TrimRight(strToBeSigned.String(), "&")
|
||||
return check(s, n.Sign, []byte(publicKeyPEM))
|
||||
}
|
||||
|
||||
func check(data, signature string, publicKeyPEM []byte) (bool, error) {
|
||||
block, _ := pem.Decode(publicKeyPEM)
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("failed to parse DER encoded public key")
|
||||
}
|
||||
|
||||
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse DER encoded public key: %v", err)
|
||||
}
|
||||
|
||||
rsaPubKey, ok := pubKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("failed to parse DER encoded public key: %v", err)
|
||||
}
|
||||
|
||||
hashed := sha256.Sum256([]byte(data))
|
||||
sig, err := base64.StdEncoding.DecodeString(signature)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to decode signature: %v", err)
|
||||
}
|
||||
|
||||
err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, hashed[:], sig)
|
||||
if err != nil {
|
||||
fmt.Println("signature verification error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package vo
|
||||
|
||||
type Code string
|
||||
|
||||
const CodeSuccess Code = "10000"
|
||||
|
||||
func (c Code) IsSuccess() bool {
|
||||
return c == CodeSuccess
|
||||
}
|
10
go.mod
10
go.mod
|
@ -3,7 +3,7 @@ module gitea.cdlsxd.cn/BaseSystem/plugin
|
|||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4
|
||||
github.com/carlmjohnson/requests v0.24.2
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/hashicorp/go-plugin v1.6.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
@ -24,9 +24,9 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.10 // indirect
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
)
|
||||
|
|
20
go.sum
20
go.sum
|
@ -1,9 +1,9 @@
|
|||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/carlmjohnson/requests v0.24.2 h1:JDakhAmTIKL/qL/1P7Kkc2INGBJIkIFP6xUeUmPzLso=
|
||||
github.com/carlmjohnson/requests v0.24.2/go.mod h1:duYA/jDnyZ6f3xbcF5PpZ9N8clgopubP2nK5i6MVMhU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
|
@ -46,16 +46,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
|
|
Loading…
Reference in New Issue