You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wechatpayopen/sign.go

165 lines
3.7 KiB

package wechatpayopen
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"go.dtapp.net/gorandom"
"net/url"
"time"
)
// 对消息的散列值进行数字签名
func (c *Client) signPKCS1v15(msg string, privateKey []byte) ([]byte, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key decode error")
}
pri, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, errors.New("parse private key error")
}
key, ok := pri.(*rsa.PrivateKey)
if ok == false {
return nil, errors.New("private key format error")
}
sign, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, c.haSha256(msg))
if err != nil {
return nil, errors.New("sign error")
}
return sign, nil
}
// base编码
func (c *Client) base64EncodeStr(src []byte) string {
return base64.StdEncoding.EncodeToString(src)
}
// sha256加密
func (c *Client) haSha256(str string) []byte {
h := sha256.New()
h.Write([]byte(str))
return h.Sum(nil)
}
// 生成身份认证信息
// https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng
func (c *Client) authorization(method string, paramMap map[string]interface{}, rawUrl string) (token string, err error) {
// 请求报文主体
var signBody string
if len(paramMap) != 0 {
paramJsonBytes, err := json.Marshal(paramMap)
if err != nil {
return token, err
}
signBody = string(paramJsonBytes)
}
// URL
urlPart, err := url.Parse(rawUrl)
if err != nil {
return token, err
}
canonicalUrl := urlPart.RequestURI()
// 请求时间戳
timestamp := time.Now().Unix()
// 请求随机串
nonce := gorandom.Alphanumeric(32)
// 构造签名串
message := fmt.Sprintf(SignatureMessageFormat, method, canonicalUrl, timestamp, nonce, signBody)
sign, err := c.signSHA256WithRSA(message, c.getRsa([]byte(c.GetMchSslKey())))
if err != nil {
return token, err
}
authorization := fmt.Sprintf(
HeaderAuthorizationFormat, getAuthorizationType(),
c.GetSpMchId(), nonce, timestamp, c.GetMchSslSerialNo(), sign,
)
return authorization, nil
}
// 报文解密
func (c *Client) decryptGCM(aesKey, nonceV, ciphertextV, additionalDataV string) ([]byte, error) {
key := []byte(aesKey)
nonce := []byte(nonceV)
additionalData := []byte(additionalDataV)
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextV)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
plaintext, err := aesGCM.Open(nil, nonce, ciphertext, additionalData)
if err != nil {
return nil, err
}
return plaintext, err
}
// 对消息的散列值进行数字签名
func (c *Client) getRsa(privateKey []byte) *rsa.PrivateKey {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil
}
pri, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil
}
key, ok := pri.(*rsa.PrivateKey)
if ok == false {
return key
}
return key
}
// 通过私钥对字符串以 SHA256WithRSA 算法生成签名信息
func (c *Client) 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
}