设为首页 收藏本站
查看: 814|回复: 0

[经验分享] 微信消息加解密(GoLang)

[复制链接]

尚未签到

发表于 2018-9-20 13:19:10 | 显示全部楼层 |阅读模式
package wechat  //微信消息加解密工具包
  const (
  //以下均为公众号管理后台设置项
  token          = "XXXXXXXX"
  appID          = "XXXXXXXXXX"
  encodingAESKey = "XXXXXXXXXXXXXXX"
  )
  var AesKey []byte
  func EncodingAESKey2AESKey(encodingKey string) []byte {
  data, _ := base64.StdEncoding.DecodeString(encodingKey + "=")
  return data
  }
  func init() {
  AesKey = EncodingAESKey2AESKey(encodingAESKey)
  }
  type TextRequestBody struct {
  XMLName      xml.Name `xml:"xml"`
  ToUserName   string
  FromUserName string
  CreateTime   time.Duration
  MsgType      string
  Url          string
  PicUrl       string
  MediaId      string
  ThumbMediaId string
  Content      string
  MsgId        int
  Location_X   string
  Location_Y   string
  Label        string
  }
  type TextResponseBody struct {
  XMLName      xml.Name `xml:"xml"`
  ToUserName   CDATAText
  FromUserName CDATAText
  CreateTime   string
  MsgType      CDATAText
  Content      CDATAText
  }
  type EncryptRequestBody struct {
  XMLName    xml.Name `xml:"xml"`
  ToUserName string
  Encrypt    string
  }
  type EncryptResponseBody struct {
  XMLName      xml.Name `xml:"xml"`
  Encrypt      CDATAText
  MsgSignature CDATAText
  TimeStamp    string
  Nonce        CDATAText
  }
  type EncryptResponseBody1 struct {
  XMLName      xml.Name `xml:"xml"`
  Encrypt      string
  MsgSignature string
  TimeStamp    string
  Nonce        string
  }
  type CDATAText struct {
  Text string `xml:",innerxml"`
  }
  func MakeSignature(timestamp, nonce string) string {
  sl := []string{token, timestamp, nonce}
  sort.Strings(sl)
  s := sha1.New()
  io.WriteString(s, strings.Join(sl, ""))
  return fmt.Sprintf("%x", s.Sum(nil))
  }
  func MakeMsgSignature(timestamp, nonce, msg_encrypt string) string {
  sl := []string{token, timestamp, nonce, msg_encrypt}
  sort.Strings(sl)
  s := sha1.New()
  io.WriteString(s, strings.Join(sl, ""))
  return fmt.Sprintf("%x", s.Sum(nil))
  }
  func ValidateUrl(timestamp, nonce, signatureIn string) bool {
  signatureGen := MakeSignature(timestamp, nonce)
  if signatureGen != signatureIn {
  return false
  }
  return true
  }
  func ValidateMsg(timestamp, nonce, msgEncrypt, msgSignatureIn string) bool {
  msgSignatureGen := MakeMsgSignature(timestamp, nonce, msgEncrypt)
  if msgSignatureGen != msgSignatureIn {
  return false
  }
  return true
  }
  func ParseEncryptRequestBody(r *http.Request) *EncryptRequestBody {
  body, err := ioutil.ReadAll(r.Body)
  if err != nil {
  return nil
  }
  //  mlog.AppendObj(nil, "Wechat Message Service: RequestBody--", body)
  requestBody := &EncryptRequestBody{}
  xml.Unmarshal(body, requestBody)
  return requestBody
  }
  func ParseTextRequestBody(r *http.Request) *TextRequestBody {
  body, err := ioutil.ReadAll(r.Body)
  r.Body.Close()
  if err != nil {
  log.Fatal(err)
  return nil
  }
  requestBody := &TextRequestBody{}
  xml.Unmarshal(body, requestBody)
  return requestBody
  }
  func Value2CDATA(v string) CDATAText {
  //return CDATAText{[]byte("")}
  return CDATAText{""}
  }
  func MakeTextResponseBody(fromUserName, toUserName, content string) ([]byte, error) {
  textResponseBody := &TextResponseBody{}
  textResponseBody.FromUserName = Value2CDATA(fromUserName)
  textResponseBody.ToUserName = Value2CDATA(toUserName)
  textResponseBody.MsgType = Value2CDATA("text")
  textResponseBody.Content = Value2CDATA(content)
  textResponseBody.CreateTime = strconv.Itoa(int(time.Duration(time.Now().Unix())))
  return xml.MarshalIndent(textResponseBody, " ", "  ")
  }
  func MakeEncryptResponseBody(fromUserName, toUserName, content, nonce, timestamp string) ([]byte, error) {
  encryptBody := &EncryptResponseBody{}
  encryptXmlData, _ := MakeEncryptXmlData(fromUserName, toUserName, timestamp, content)
  encryptBody.Encrypt = Value2CDATA(encryptXmlData)
  encryptBody.MsgSignature = Value2CDATA(MakeMsgSignature(timestamp, nonce, encryptXmlData))
  encryptBody.TimeStamp = timestamp
  encryptBody.Nonce = Value2CDATA(nonce)
  return xml.MarshalIndent(encryptBody, " ", "  ")
  }
  func MakeEncryptXmlData(fromUserName, toUserName, timestamp, content string) (string, error) {
  textResponseBody := &TextResponseBody{}
  textResponseBody.FromUserName = Value2CDATA(fromUserName)
  textResponseBody.ToUserName = Value2CDATA(toUserName)
  textResponseBody.MsgType = Value2CDATA("text")
  textResponseBody.Content = Value2CDATA(content)
  textResponseBody.CreateTime = timestamp
  body, err := xml.MarshalIndent(textResponseBody, " ", "  ")
  if err != nil {
  return "", errors.New("xml marshal error")
  }
  buf := new(bytes.Buffer)
  err = binary.Write(buf, binary.BigEndian, int32(len(body)))
  if err != nil {
  mlog.AppendObj(err, "Binary write err:", err)
  }
  bodyLength := buf.Bytes()
  randomBytes := []byte("abcdefghijklmnop")
  plainData := bytes.Join([][]byte{randomBytes, bodyLength, body, []byte(appID)}, nil)
  cipherData, err := AesEncrypt(plainData, AesKey)
  if err != nil {
  return "", errors.New("AesEncrypt error")
  }
  return base64.StdEncoding.EncodeToString(cipherData), nil
  }
  // PadLength calculates padding length, from github.com/vgorin/cryptogo
  func PadLength(slice_length, blocksize int) (padlen int) {
  padlen = blocksize - slice_length%blocksize
  if padlen == 0 {
  padlen = blocksize
  }
  return padlen
  }
  //from github.com/vgorin/cryptogo
  func PKCS7Pad(message []byte, blocksize int) (padded []byte) {

  // block>  if blocksize < 1  }
  func AesEncrypt(plainData []byte, aesKey []byte) ([]byte, error) {
  k := len(aesKey)
  if len(plainData)%k != 0 {
  plainData = PKCS7Pad(plainData, k)
  }
  block, err := aes.NewCipher(aesKey)
  if err != nil {
  return nil, err
  }
  iv := make([]byte, aes.BlockSize)
  if _, err := io.ReadFull(rand.Reader, iv); err != nil {
  return nil, err
  }
  cipherData := make([]byte, len(plainData))
  blockMode := cipher.NewCBCEncrypter(block, iv)
  blockMode.CryptBlocks(cipherData, plainData)
  return cipherData, nil
  }
  func AesDecrypt(cipherData []byte, aesKey []byte) ([]byte, error) {
  k := len(aesKey) //PKCS#7
  if len(cipherData)%k != 0 {

  return nil, errors.New("crypto/cipher: ciphertext>  }
  block, err := aes.NewCipher(aesKey)
  if err != nil {
  return nil, err
  }
  iv := make([]byte, aes.BlockSize)
  if _, err := io.ReadFull(rand.Reader, iv); err != nil {
  return nil, err
  }
  blockMode := cipher.NewCBCDecrypter(block, iv)
  plainData := make([]byte, len(cipherData))
  blockMode.CryptBlocks(plainData, cipherData)
  return plainData, nil
  }
  func ValidateAppId(id []byte) bool {
  if string(id) == appID {
  return true
  }
  return false
  }
  func ParseEncryptTextRequestBody(plainText []byte) (*TextRequestBody, error) {
  // Read length
  buf := bytes.NewBuffer(plainText[16:20])
  var length int32
  binary.Read(buf, binary.BigEndian, &length)
  // appID validation
  appIDstart := 20 + length
  id := plainText[appIDstart : int(appIDstart)+len(appID)]
  if !ValidateAppId(id) {
  mlog.AppendObj(nil, "Wechat Message Service: appid is invalid!")
  return nil, errors.New("Appid is invalid")
  }
  mlog.AppendObj(nil, "Wechat Message Service: appid validation is ok!")
  textRequestBody := &TextRequestBody{}
  xml.Unmarshal(plainText[20:20+length], textRequestBody)
  return textRequestBody, nil
  }
  func ParseEncryptResponse(responseEncryptTextBody []byte) {
  textResponseBody := &EncryptResponseBody1{}
  xml.Unmarshal(responseEncryptTextBody, textResponseBody)
  if !ValidateMsg(textResponseBody.TimeStamp, textResponseBody.Nonce, textResponseBody.Encrypt, textResponseBody.MsgSignature) {
  mlog.AppendInfo("msg signature is invalid")
  return
  }
  cipherData, err := base64.StdEncoding.DecodeString(textResponseBody.Encrypt)
  if err != nil {
  mlog.AppendObj(err, "Wechat Message Service: Decode base64 error")
  return
  }
  plainText, err := AesDecrypt(cipherData, AesKey)
  if err != nil {
  mlog.AppendInfo(err)
  return
  }
  mlog.AppendInfo(string(plainText))
  }
  func DecryptWechatAppletUser(encryptedData string, session_key string, iv string) ([]byte, error) {
  ciphertext, _ := base64.StdEncoding.DecodeString(encryptedData)
  key, _ := base64.StdEncoding.DecodeString(session_key)
  keyBytes := []byte(key)
  block, err := aes.NewCipher(keyBytes) //选择加密算法
  if err != nil {
  return nil, err
  }
  iv_b, _ := base64.StdEncoding.DecodeString(iv)
  blockModel := cipher.NewCBCDecrypter(block, iv_b)
  plantText := make([]byte, len(ciphertext))
  blockModel.CryptBlocks(plantText, ciphertext)
  plantText = PKCS7UnPadding(plantText, block.BlockSize())
  return plantText, nil
  }
  func PKCS7UnPadding(plantText []byte, blockSize int) []byte {
  length := len(plantText)
  unpadding := int(plantText[length-1])
  return plantText[:(length - unpadding)]
  }


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-598910-1-1.html 上篇帖子: Golang面向对象编程 下篇帖子: golang的垃圾回收(GC)机制
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表