-
Notifications
You must be signed in to change notification settings - Fork 156
/
signer.go
188 lines (161 loc) · 5.63 KB
/
signer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package support
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"github.com/ArtisanCloud/PowerLibs/v3/object"
"io/ioutil"
"os"
"strings"
"time"
)
// 请求报文签名相关常量
const (
SignatureMessageFormat = "%s\n%s\n%s\n%s\n%s\n" // 数字签名原文格式
// HeaderAuthorizationFormat 请求头中的 Authorization 拼接格式
HeaderAuthorizationFormat = "%s mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""
)
// SHA256WithRSASigner Sha256WithRSA 数字签名生成器
type SHA256WithRSASigner struct {
MchID string // 商户号
CertificateSerialNo string // 商户证书序列号
PrivateKeyPath string // 商户私钥路径,会自动读取出*rsa.PrivateKey
PrivateKey *rsa.PrivateKey // 商户私钥
}
// SignatureResult 数字签名结果
type SignatureResult struct {
MchID string // 商户号
CertificateSerialNo string // 签名对应的证书序列号
Signature string // 签名内容
}
type RequestSignChain struct {
Method string // 接口提交方法。http.MethodPost, http.MethodPost等
CanonicalURL string // 微信支付接口路径。 例如: /v3/pay/transactions/jsapi
SignBody string // 提交的body字符串。 例如; {"amount":{"total":1},"appid":"ww16143ea0101327c7","attach":"自定义数据说明","description":"Image形象店-深圳腾大-QQ公仔","mchid":"1611854986","notify_url":"https://pay.wangchaoyi.com/wx/notify","out_trade_no":"5519778939773395659222199361","payer":{"openid":"oAuaP0TRUMwP169nQfg7XCEAw3HQ"}}
timestamp int64 // 单元测试传入的固定时间戳
nonce string // 单元测试传入的固定随机数
}
func (s *SHA256WithRSASigner) GenerateRequestSign(signChain *RequestSignChain) (authorization string, err error) {
timestamp := time.Now().Unix()
nonce := object.QuickRandom(32)
//timestamp := int64(1689057609)
//nonce := "YXkkuNGrgWVS1ucj4KHmayKSUHFkTfzO"
// Under ci mode, go fixed value
// 在ci模式下面,走固定值
if signChain.timestamp != 0 && signChain.nonce != "" {
timestamp = signChain.timestamp
nonce = signChain.nonce
}
// Splice the string to be signed
// 拼接出需要签名的字符串
arrayParams := []string{signChain.Method, signChain.CanonicalURL, fmt.Sprintf("%d", timestamp), nonce, signChain.SignBody}
message := strings.Join(arrayParams, "\n") + "\n"
// sign the uniformMessage
//fmt2.Dump(message, len(message))
signatureResult, err := s.Sign(context.TODO(), message)
//fmt2.Dump(signatureResult)
if err != nil {
return "", err
}
// get the header authorization
authorization = fmt.Sprintf(
HeaderAuthorizationFormat,
s.GetAuthorizationType(),
signatureResult.MchID,
nonce,
timestamp,
signatureResult.CertificateSerialNo,
signatureResult.Signature,
)
return authorization, err
}
func (s *SHA256WithRSASigner) GenerateSign(message string) (sign string, err error) {
// sign the uniformMessage
signatureResult, err := s.Sign(context.TODO(), message)
if err != nil {
return "", err
}
return signatureResult.Signature, err
}
// Sign 对信息使用 SHA256WithRSA 算法进行签名
func (s *SHA256WithRSASigner) Sign(_ context.Context, message string) (*SignatureResult, error) {
if s.PrivateKey == nil && s.PrivateKeyPath == "" {
return nil, fmt.Errorf("you must set privatekey to use SHA256WithRSASigner")
}
// Parsing the certificate inside the PrivateKeyPath
// 读取PrivateKeyPath里面的证书
if s.PrivateKey == nil {
privateKey, err := s.getPrivateKey()
if err != nil {
return nil, err
}
s.PrivateKey = privateKey
}
if strings.TrimSpace(s.CertificateSerialNo) == "" {
return nil, fmt.Errorf("you must set mch certificate serial no to use SHA256WithRSASigner")
}
signature, err := SignSHA256WithRSA(message, s.PrivateKey)
if err != nil {
return nil, err
}
return &SignatureResult{MchID: s.MchID, CertificateSerialNo: s.CertificateSerialNo, Signature: signature}, nil
}
func (s *SHA256WithRSASigner) getPrivateKey() (*rsa.PrivateKey, error) {
var keyBytes []byte
var err error
if fileInfo, err := os.Stat(s.PrivateKeyPath); err == nil && !fileInfo.IsDir() {
// 读取私钥文件
keyBytes, err = ioutil.ReadFile(s.PrivateKeyPath)
if err != nil {
return nil, err
}
} else {
// 使用私钥内容字符串
keyBytes = []byte(s.PrivateKeyPath)
}
// 解码私钥
privPem, _ := pem.Decode(keyBytes)
if privPem == nil {
return nil, fmt.Errorf("failed to decode private key")
}
// 解析私钥
parsedKey, err := x509.ParsePKCS8PrivateKey(privPem.Bytes)
if err != nil {
return nil, err
}
// 转换为 *rsa.PrivateKey 类型
privateKey, ok := parsedKey.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("%s is not an RSA private key", s.PrivateKeyPath)
}
return privateKey, nil
}
// Algorithm 返回使用的签名算法:SHA256-RSA2048
func (s *SHA256WithRSASigner) Algorithm() string {
return "SHA256-RSA2048"
}
func (s *SHA256WithRSASigner) GetAuthorizationType() string {
return "WECHATPAY2-" + s.Algorithm()
}
// SignSHA256WithRSA 通过私钥对字符串以 SHA256WithRSA 算法生成签名信息
func SignSHA256WithRSA(source string, privateKey *rsa.PrivateKey) (signature string, err error) {
if privateKey == nil {
return "", fmt.Errorf("private key should not be nil")
}
h := crypto.Hash.New(crypto.SHA256)
_, err = h.Write([]byte(source))
if err != nil {
return "", nil
}
hashed := h.Sum(nil)
signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signatureByte), nil
}