|
- package wechat
-
- import (
- "bytes"
- "encoding/base64"
- "encoding/binary"
- "encoding/xml"
- "errors"
- "fmt"
- )
-
- type msgCrypt struct {
- token string
- aesKey string
- appid string
- }
-
- // EventEncryptRequest 微信事件推送结构体
- type EventEncryptRequest struct {
- XMLName xml.Name `xml:"xml"`
- Encrypt string `xml:"Encrypt"`
- Appid string `xml:"Appid"`
- }
-
- // MessageEncryptRequest 微信消息加密结构体
- type MessageEncryptRequest struct {
- XMLName xml.Name `xml:"xml"`
- Encrypt string `xml:"Encrypt"`
- MsgSignature string `xml:"MsgSignature"`
- TimeStamp string `xml:"TimeStamp"`
- Nonce string `xml:"Nonce"`
- }
-
- // NewWechatMsgCrypt 实例化微信加解密
- func NewWechatMsgCrypt(token string, aesKey string, appid string) *msgCrypt {
- instance := new(msgCrypt)
- instance.token = token
- instance.aesKey = aesKey
- instance.appid = appid
- return instance
- }
-
- // WechatEventDecrypt 微信事件推送解密
- func (w *msgCrypt) WechatEventDecrypt(eventRequest EventEncryptRequest, msgSignature string, timestamp, nonce string) EventMessageBody {
- errCode, data := w.decryptMsg(msgSignature, timestamp, nonce, eventRequest.Encrypt)
- if errCode != SuccessCode {
- panic(fmt.Sprintf("消息解密失败,code:%d", errCode))
- }
- message := EventMessageBody{}
- _, err := FormatMessage(data, &message)
- if err != nil {
- panic(fmt.Sprintf("消息格式化失败,%s", err.Error()))
- }
- return message
- }
-
- // WechatMessageDecrypt 微信消息解密
- func (w *msgCrypt) WechatMessageDecrypt(messageEncryptRequest MessageEncryptRequest) interface{} {
- errCode, data := w.decryptMsg(messageEncryptRequest.MsgSignature, messageEncryptRequest.TimeStamp, messageEncryptRequest.Nonce, messageEncryptRequest.Encrypt)
- if errCode != SuccessCode {
- panic(fmt.Sprintf("消息解密失败,code:%d", errCode))
- }
- message := MessageBodyDecrypt{}
- _, err := FormatMessage(data, &message)
- if err != nil {
- panic(fmt.Sprintf("消息格式化失败,%s", err.Error()))
- }
- return message
- }
-
- // WechatTextMessage 微信文本消息加密
- func (w *msgCrypt) WechatTextMessage(fromUserName string, toUserName string, content string) MessageEncryptBody {
- message := FormatTextMessage(fromUserName, toUserName, content)
- encrypted, err := w.encryptMsg(message)
- if err != nil {
- panic("消息加密失败:" + err.Error())
- }
- data := FormatEncryptData(encrypted, w.token)
- return data
- }
-
- // WechatImageMessage 微信图片消息加密
- func (w *msgCrypt) WechatImageMessage(fromUserName string, toUserName string, mediaId string) MessageEncryptBody {
- message := FormatImageMessage(fromUserName, toUserName, mediaId)
- encrypted, err := w.encryptMsg(message)
- if err != nil {
- panic("消息加密失败:" + err.Error())
- }
- data := FormatEncryptData(encrypted, w.token)
- return data
- }
-
- // WechatVoiceMessage 微信语音消息加密
- func (w *msgCrypt) WechatVoiceMessage(fromUserName string, toUserName string, mediaId string) MessageEncryptBody {
- message := FormatVoiceMessage(fromUserName, toUserName, mediaId)
- encrypted, err := w.encryptMsg(message)
- if err != nil {
- panic("消息加密失败:" + err.Error())
- }
- data := FormatEncryptData(encrypted, w.token)
- return data
- }
-
- // WechatVideoMessage 微信视频消息加密
- func (w *msgCrypt) WechatVideoMessage(fromUserName string, toUserName string, mediaId string, title string, description string) MessageEncryptBody {
- message := FormatVideoMessage(fromUserName, toUserName, mediaId, title, description)
- encrypted, err := w.encryptMsg(message)
- if err != nil {
- panic("消息加密失败:" + err.Error())
- }
- data := FormatEncryptData(encrypted, w.token)
- return data
- }
-
- // WechatMusicMessage 微信音乐消息加密
- func (w *msgCrypt) WechatMusicMessage(fromUserName string, toUserName string, thumbMediaId string, title string, description string, musicUrl string, hQMusicUrl string) MessageEncryptBody {
- message := FormatMusicMessage(fromUserName, toUserName, thumbMediaId, title, description, musicUrl, hQMusicUrl)
- encrypted, err := w.encryptMsg(message)
- if err != nil {
- panic("消息加密失败:" + err.Error())
- }
- data := FormatEncryptData(encrypted, w.token)
- return data
- }
-
- // WechatArticlesMessage 微信图文消息加密
- func (w *msgCrypt) WechatArticlesMessage(fromUserName string, toUserName string, items []ArticleItem) MessageEncryptBody {
- message := FormatArticlesMessage(fromUserName, toUserName, items)
- encrypted, err := w.encryptMsg(message)
- if err != nil {
- panic("消息加密失败:" + err.Error())
- }
- data := FormatEncryptData(encrypted, w.token)
- return data
- }
-
- // decryptMsg aes消息解密
- func (w *msgCrypt) decryptMsg(msgSignature string, timestamp, nonce string, encrypted string) (int, []byte) {
- //验证aes
- if len(w.aesKey) != 43 {
- return IllegalAesKey, nil
- }
- //判断签名是否一致
- if err := w.validSignature(msgSignature, timestamp, nonce, encrypted); err != nil {
- return ValidateSignatureError, nil
- }
- //解密
- prp := NewPrpCrypt(w.aesKey)
- plainText, err := prp.decrypt(encrypted)
- if err != nil {
- return DecryptAESError, nil
- }
- //验证appid是否一致(消息来源是否一致)
- if err := w.validMessageSource(plainText); err != nil {
- return ValidateAppidError, nil
- }
- return SuccessCode, plainText
- }
-
- // encryptMsg aes消息加密
- func (w *msgCrypt) encryptMsg(message []byte) (string, error) {
- //计算消息长度
- buf := new(bytes.Buffer)
- err := binary.Write(buf, binary.BigEndian, int32(len(message)))
- if err != nil {
- return "", err
- }
- messageLength := buf.Bytes()
- //生成随机字符串
- randBytes := []byte(makeRandomString(16))
- plainData := bytes.Join([][]byte{randBytes, messageLength, message, []byte(w.appid)}, nil)
- prp := NewPrpCrypt(w.aesKey)
- //消息加密
- encrypted, err := prp.encrypt(plainData)
- if err != nil {
- return "", err
- }
- return base64.StdEncoding.EncodeToString(encrypted), nil
- }
-
- // validSignature 验证签名是否一致
- func (w *msgCrypt) validSignature(msgSignature string, timestamp, nonce string, encrypted string) error {
- validSignature := GetSignature(timestamp, nonce, encrypted, w.token)
- if validSignature != msgSignature {
- return errors.New("签名不一致:valid sign error")
- }
- return nil
- }
-
- // validMessageSource 验证消息来源
- func (w *msgCrypt) validMessageSource(plainText []byte) error {
- messageLength := GetMessageLength(plainText)
- //获取appid位置
- appIdStartPos := 20 + messageLength
- id := plainText[appIdStartPos : int(appIdStartPos)+len(w.appid)]
- if string(id) != w.appid {
- return errors.New("消息来源不一致:Appid is invalid")
- }
- return nil
- }
|