forked from chanxuehong/wechat
/
auth.go
171 lines (142 loc) · 4.12 KB
/
auth.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
package mchv3
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"math/rand"
"net/http"
"strings"
"time"
)
type CertResponse struct {
Data []struct {
SerialNo string `json:"serial_no"` // 证书序列号
EffectiveTime string `json:"effective_time"` // 生效时间
ExpireTime string `json:"expire_time"` // 过期时间
EncryptCertificate EncryptData `json:"encrypt_certificate"` // 证书内容
} `json:"data"`
}
// 获取微信支付平台证书
// 平台证书会周期性更换。建议商户定时通过API下载新的证书
// 可以弄个Cronjob线程周期执行这个方法
func (cli *Client) GetWechatCertificate() (err error) {
var certResp *CertResponse
var resp []byte
var pblock *pem.Block
var cert *x509.Certificate
resp, err = cli.DoGet("/v3/certificates")
if err != nil {
return
}
certResp = new(CertResponse)
err = json.Unmarshal(resp, certResp)
if err != nil {
return
}
plaintext, err := cli.DecryptData(certResp.Data[0].EncryptCertificate)
if err != nil {
return
}
// if DEBUG {
// fmt.Printf("%s\n", string(plaintext))
// }
// 加载证书
pblock, _ = pem.Decode([]byte(plaintext))
cert, err = x509.ParseCertificate(pblock.Bytes)
if err != nil {
return
}
cli.WechatCertificate = cert
cli.WechatSerialNumber = strings.ToUpper(cert.SerialNumber.Text(16))
return
}
func (cli *Client) DecryptData(encryptData EncryptData) (data []byte, err error) {
nonce := []byte(encryptData.Nonce)
ciphertext, err := base64.StdEncoding.DecodeString(encryptData.Ciphertext)
if err != nil {
return
}
ad := []byte(encryptData.AssociatedData)
// 解密过程 https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi
block, err := aes.NewCipher([]byte(cli.AppSecret))
if err != nil {
return
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return
}
data, err = aesgcm.Open(nil, nonce, ciphertext, ad)
if err != nil {
return
}
return
}
// 加签
// https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng
func (cli *Client) Sign(method, url, body, nonce string) (sign string, err error) {
string2sign := method + "\n" + url + "\n" + timestamp() + "\n" + nonce + "\n" + body + "\n"
return cli.SignStr(string2sign)
}
func (cli *Client) SignStr(str string) (sign string, err error) {
digest := sha256.Sum256([]byte(str))
hashed, err := rsa.SignPKCS1v15(nil, cli.PrivateKey, crypto.SHA256, digest[:])
if err != nil {
return
}
sign = base64.StdEncoding.EncodeToString(hashed)
return
}
// 验签
// https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-yan-zheng
func (cli *Client) Verify(resp *http.Response, body []byte) bool {
var wechatpaySerial, wechatpayTimestamp, wechatpayNonce, wechatpaySignature string
var sig, digest []byte
var err error
var string2verify string
wechatpaySerial = resp.Header.Get("Wechatpay-Serial")
wechatpayTimestamp = resp.Header.Get("Wechatpay-Timestamp")
wechatpayNonce = resp.Header.Get("Wechatpay-Nonce")
wechatpaySignature = resp.Header.Get("Wechatpay-Signature")
if wechatpaySerial != cli.WechatSerialNumber {
fmt.Println(wechatpaySerial, cli.WechatSerialNumber)
return false
}
string2verify = fmt.Sprintf("%s\n%s\n%s\n", wechatpayTimestamp, wechatpayNonce, string(body))
// if DEBUG {
// fmt.Println(digest)
// }
h := sha256.New()
h.Write([]byte(string2verify))
digest = h.Sum(nil)
sig, _ = base64.StdEncoding.DecodeString(wechatpaySignature)
err = rsa.VerifyPKCS1v15((cli.WechatCertificate.PublicKey).(*rsa.PublicKey), crypto.SHA256, digest, sig)
if err != nil {
fmt.Println("verify error:", err)
return false
}
return true
}
func genRandomString(size int) string {
var a, b []byte
var i int
a = []byte("1234567890abcdefghijklmnopqrstuvwxyz")
b = make([]byte, size)
for i = 0; i < size; i++ {
b[i] = a[rand.Intn(36)]
}
return string(b)
}
func timestamp() string {
// if DEBUG {
// return "1582883827"
// }
return fmt.Sprintf("%d", time.Now().Unix())
}