小程序可在 Go 上完成微信手机号登录、测评、预约和下单,不再反代 Java。 Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.0 KiB
Go
76 lines
2.0 KiB
Go
package wechat
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
type phonePayload struct {
|
|
PhoneNumber string `json:"phoneNumber"`
|
|
PurePhoneNumber string `json:"purePhoneNumber"`
|
|
Watermark struct {
|
|
AppID string `json:"appid"`
|
|
} `json:"watermark"`
|
|
}
|
|
|
|
// DecryptPhone decrypts WeChat getPhoneNumber encryptedData (AES-128-CBC).
|
|
func DecryptPhone(sessionKey, encryptedData, iv, expectAppID string) (string, error) {
|
|
key, err := base64.StdEncoding.DecodeString(strings.TrimSpace(sessionKey))
|
|
if err != nil || len(key) != 16 {
|
|
return "", errors.New("无效的 session_key")
|
|
}
|
|
raw, err := base64.StdEncoding.DecodeString(strings.TrimSpace(encryptedData))
|
|
if err != nil || len(raw) == 0 || len(raw)%aes.BlockSize != 0 {
|
|
return "", errors.New("无效的加密数据")
|
|
}
|
|
ivb, err := base64.StdEncoding.DecodeString(strings.TrimSpace(iv))
|
|
if err != nil || len(ivb) != aes.BlockSize {
|
|
return "", errors.New("无效的 iv")
|
|
}
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
plain := make([]byte, len(raw))
|
|
cipher.NewCBCDecrypter(block, ivb).CryptBlocks(plain, raw)
|
|
plain, err = pkcs7Unpad(plain)
|
|
if err != nil {
|
|
return "", errors.New("解密失败")
|
|
}
|
|
var p phonePayload
|
|
if err := json.Unmarshal(plain, &p); err != nil {
|
|
return "", errors.New("解密失败")
|
|
}
|
|
if expectAppID != "" && p.Watermark.AppID != "" && p.Watermark.AppID != expectAppID {
|
|
return "", errors.New("微信数据校验失败")
|
|
}
|
|
phone := strings.TrimSpace(p.PhoneNumber)
|
|
if phone == "" {
|
|
phone = strings.TrimSpace(p.PurePhoneNumber)
|
|
}
|
|
if phone == "" {
|
|
return "", errors.New("未获取到手机号")
|
|
}
|
|
return phone, nil
|
|
}
|
|
|
|
func pkcs7Unpad(b []byte) ([]byte, error) {
|
|
if len(b) == 0 {
|
|
return nil, errors.New("empty")
|
|
}
|
|
n := int(b[len(b)-1])
|
|
if n == 0 || n > len(b) {
|
|
return nil, errors.New("pad")
|
|
}
|
|
for i := 0; i < n; i++ {
|
|
if b[len(b)-1-i] != byte(n) {
|
|
return nil, errors.New("pad")
|
|
}
|
|
}
|
|
return b[:len(b)-n], nil
|
|
}
|