package wechat import ( "crypto/aes" "crypto/cipher" "crypto/sha1" "encoding/base64" "encoding/hex" "encoding/json" "fmt" ) // DecryptedUserInfo 解密后的用户信息结构 type DecryptedUserInfo struct { OpenID string `json:"openId"` NickName string `json:"nickName"` Gender int `json:"gender"` City string `json:"city"` Province string `json:"province"` Country string `json:"country"` AvatarURL string `json:"avatarUrl"` UnionID string `json:"unionId,omitempty"` Watermark Watermark `json:"watermark"` } // Watermark 数据水印 type Watermark struct { AppID string `json:"appid"` Timestamp int64 `json:"timestamp"` } // DecryptData 解密微信小程序加密数据 // sessionKey: 会话密钥(从 code2session 接口获取) // encryptedData: 加密数据(Base64 编码) // iv: 初始向量(Base64 编码) // 返回解密后的 JSON 字符串 func DecryptData(sessionKey, encryptedData, iv string) (string, error) { // 1. Base64 解码 session_key aesKey, err := base64.StdEncoding.DecodeString(sessionKey) if err != nil { return "", fmt.Errorf("session_key base64 解码失败: %v", err) } // 2. Base64 解码加密数据 cipherText, err := base64.StdEncoding.DecodeString(encryptedData) if err != nil { return "", fmt.Errorf("encryptedData base64 解码失败: %v", err) } // 3. Base64 解码初始向量 ivBytes, err := base64.StdEncoding.DecodeString(iv) if err != nil { return "", fmt.Errorf("iv base64 解码失败: %v", err) } // 4. 验证密钥长度(AES-128 需要 16 字节) if len(aesKey) != 16 { return "", fmt.Errorf("session_key 长度错误,期望 16 字节,实际 %d 字节", len(aesKey)) } // 5. 验证 IV 长度(AES 块大小为 16 字节) if len(ivBytes) != 16 { return "", fmt.Errorf("iv 长度错误,期望 16 字节,实际 %d 字节", len(ivBytes)) } // 6. 验证密文长度(必须是 AES 块大小的倍数) if len(cipherText)%aes.BlockSize != 0 { return "", fmt.Errorf("密文长度错误,必须是 %d 字节的倍数,实际 %d 字节", aes.BlockSize, len(cipherText)) } // 7. 创建 AES 解密器 block, err := aes.NewCipher(aesKey) if err != nil { return "", fmt.Errorf("创建 AES 解密器失败: %v", err) } // 8. 创建 CBC 模式解密器 mode := cipher.NewCBCDecrypter(block, ivBytes) // 9. 解密数据 decrypted := make([]byte, len(cipherText)) mode.CryptBlocks(decrypted, cipherText) // 10. 去除 PKCS#7 填充 decrypted, err = pkcs7Unpad(decrypted) if err != nil { return "", fmt.Errorf("去除填充失败: %v", err) } return string(decrypted), nil } // DecryptUserInfo 解密用户信息并返回结构化数据 func DecryptUserInfo(sessionKey, encryptedData, iv string) (*DecryptedUserInfo, error) { decryptedJSON, err := DecryptData(sessionKey, encryptedData, iv) if err != nil { return nil, err } var userInfo DecryptedUserInfo if err := json.Unmarshal([]byte(decryptedJSON), &userInfo); err != nil { return nil, fmt.Errorf("解析用户信息 JSON 失败: %v", err) } return &userInfo, nil } // VerifySignature 验证数据签名 // rawData: 原始数据 // signature: 签名 // sessionKey: 会话密钥 func VerifySignature(rawData, signature, sessionKey string) bool { // 计算签名:sha1(rawData + sessionKey) h := sha1.New() h.Write([]byte(rawData + sessionKey)) expectedSignature := hex.EncodeToString(h.Sum(nil)) return expectedSignature == signature } // pkcs7Unpad 去除 PKCS#7 填充 func pkcs7Unpad(data []byte) ([]byte, error) { if len(data) == 0 { return nil, fmt.Errorf("数据为空") } // 获取填充长度 padding := int(data[len(data)-1]) // 验证填充长度 if padding > len(data) || padding == 0 { return nil, fmt.Errorf("无效的填充长度: %d", padding) } // 验证填充字节 for i := len(data) - padding; i < len(data); i++ { if data[i] != byte(padding) { return nil, fmt.Errorf("无效的填充字节") } } return data[:len(data)-padding], nil }