|
|
@@ -0,0 +1,311 @@
|
|
|
+package wxbizjsonmsgcrypt
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "crypto/aes"
|
|
|
+ "crypto/cipher"
|
|
|
+ "crypto/sha1"
|
|
|
+ "encoding/base64"
|
|
|
+ "encoding/binary"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "math/rand"
|
|
|
+ "sort"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
+
|
|
|
+const (
|
|
|
+ ValidateSignatureError int = -40001
|
|
|
+ ParseJsonError int = -40002
|
|
|
+ ComputeSignatureError int = -40003
|
|
|
+ IllegalAesKey int = -40004
|
|
|
+ ValidateCorpidError int = -40005
|
|
|
+ EncryptAESError int = -40006
|
|
|
+ DecryptAESError int = -40007
|
|
|
+ IllegalBuffer int = -40008
|
|
|
+ EncodeBase64Error int = -40009
|
|
|
+ DecodeBase64Error int = -40010
|
|
|
+ GenJsonError int = -40011
|
|
|
+ IllegalProtocolType int = -40012
|
|
|
+)
|
|
|
+
|
|
|
+type ProtocolType int
|
|
|
+
|
|
|
+const (
|
|
|
+ JsonType ProtocolType = 1
|
|
|
+)
|
|
|
+
|
|
|
+type CryptError struct {
|
|
|
+ ErrCode int
|
|
|
+ ErrMsg string
|
|
|
+}
|
|
|
+
|
|
|
+func NewCryptError(err_code int, err_msg string) *CryptError {
|
|
|
+ return &CryptError{ErrCode: err_code, ErrMsg: err_msg}
|
|
|
+}
|
|
|
+
|
|
|
+type WXBizJsonMsg4Recv struct {
|
|
|
+ Tousername string `json:"tousername"`
|
|
|
+ Encrypt string `json:"encrypt"`
|
|
|
+ Agentid string `json:"agentid"`
|
|
|
+}
|
|
|
+
|
|
|
+type WXBizJsonMsg4Send struct {
|
|
|
+ Encrypt string `json:"encrypt"`
|
|
|
+ Signature string `json:"msgsignature"`
|
|
|
+ Timestamp string `json:"timestamp"`
|
|
|
+ Nonce string `json:"nonce"`
|
|
|
+}
|
|
|
+
|
|
|
+func NewWXBizJsonMsg4Send(encrypt, signature, timestamp, nonce string) *WXBizJsonMsg4Send {
|
|
|
+ return &WXBizJsonMsg4Send{Encrypt: encrypt, Signature: signature, Timestamp: timestamp, Nonce: nonce}
|
|
|
+}
|
|
|
+
|
|
|
+type ProtocolProcessor interface {
|
|
|
+ parse(src_data []byte) (*WXBizJsonMsg4Recv, *CryptError)
|
|
|
+ serialize(msg_send *WXBizJsonMsg4Send) ([]byte, *CryptError)
|
|
|
+}
|
|
|
+
|
|
|
+type WXBizMsgCrypt struct {
|
|
|
+ token string
|
|
|
+ encoding_aeskey string
|
|
|
+ receiver_id string
|
|
|
+ protocol_processor ProtocolProcessor
|
|
|
+}
|
|
|
+
|
|
|
+type JsonProcessor struct {
|
|
|
+}
|
|
|
+
|
|
|
+func (self *JsonProcessor) parse(src_data []byte) (*WXBizJsonMsg4Recv, *CryptError) {
|
|
|
+ var msg4_recv WXBizJsonMsg4Recv
|
|
|
+ err := json.Unmarshal(src_data, &msg4_recv)
|
|
|
+ if nil != err {
|
|
|
+ fmt.Println("Unmarshal fail", err)
|
|
|
+ return nil, NewCryptError(ParseJsonError, "json to msg fail")
|
|
|
+ }
|
|
|
+ return &msg4_recv, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (self *JsonProcessor) serialize(msg4_send *WXBizJsonMsg4Send) ([]byte, *CryptError) {
|
|
|
+ json_msg, err := json.Marshal(msg4_send)
|
|
|
+ if nil != err {
|
|
|
+ return nil, NewCryptError(GenJsonError, err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ return json_msg, nil
|
|
|
+}
|
|
|
+
|
|
|
+func NewWXBizMsgCrypt(token, encoding_aeskey, receiver_id string, protocol_type ProtocolType) *WXBizMsgCrypt {
|
|
|
+ var protocol_processor ProtocolProcessor
|
|
|
+ if protocol_type != JsonType {
|
|
|
+ panic("unsupport protocal")
|
|
|
+ } else {
|
|
|
+ protocol_processor = new(JsonProcessor)
|
|
|
+ }
|
|
|
+
|
|
|
+ return &WXBizMsgCrypt{token: token, encoding_aeskey: (encoding_aeskey + "="), receiver_id: receiver_id, protocol_processor: protocol_processor}
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) randString(n int) string {
|
|
|
+ b := make([]byte, n)
|
|
|
+ for i := range b {
|
|
|
+ b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
|
|
|
+ }
|
|
|
+ return string(b)
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) pKCS7Padding(plaintext string, block_size int) []byte {
|
|
|
+ padding := block_size - (len(plaintext) % block_size)
|
|
|
+ padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
|
|
+ var buffer bytes.Buffer
|
|
|
+ buffer.WriteString(plaintext)
|
|
|
+ buffer.Write(padtext)
|
|
|
+ return buffer.Bytes()
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) pKCS7Unpadding(plaintext []byte, block_size int) ([]byte, *CryptError) {
|
|
|
+ plaintext_len := len(plaintext)
|
|
|
+ if nil == plaintext || plaintext_len == 0 {
|
|
|
+ return nil, NewCryptError(DecryptAESError, "pKCS7Unpadding error nil or zero")
|
|
|
+ }
|
|
|
+ if plaintext_len%block_size != 0 {
|
|
|
+ return nil, NewCryptError(DecryptAESError, "pKCS7Unpadding text not a multiple of the block size")
|
|
|
+ }
|
|
|
+ padding_len := int(plaintext[plaintext_len-1])
|
|
|
+ return plaintext[:plaintext_len-padding_len], nil
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) cbcEncrypter(plaintext string) ([]byte, *CryptError) {
|
|
|
+ aeskey, err := base64.StdEncoding.DecodeString(self.encoding_aeskey)
|
|
|
+ if nil != err {
|
|
|
+ return nil, NewCryptError(DecodeBase64Error, err.Error())
|
|
|
+ }
|
|
|
+ const block_size = 32
|
|
|
+ pad_msg := self.pKCS7Padding(plaintext, block_size)
|
|
|
+
|
|
|
+ block, err := aes.NewCipher(aeskey)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewCryptError(EncryptAESError, err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ ciphertext := make([]byte, len(pad_msg))
|
|
|
+ iv := aeskey[:aes.BlockSize]
|
|
|
+
|
|
|
+ mode := cipher.NewCBCEncrypter(block, iv)
|
|
|
+
|
|
|
+ mode.CryptBlocks(ciphertext, pad_msg)
|
|
|
+ base64_msg := make([]byte, base64.StdEncoding.EncodedLen(len(ciphertext)))
|
|
|
+ base64.StdEncoding.Encode(base64_msg, ciphertext)
|
|
|
+
|
|
|
+ return base64_msg, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) cbcDecrypter(base64_encrypt_msg string) ([]byte, *CryptError) {
|
|
|
+ aeskey, err := base64.StdEncoding.DecodeString(self.encoding_aeskey)
|
|
|
+ if nil != err {
|
|
|
+ return nil, NewCryptError(DecodeBase64Error, err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ encrypt_msg, err := base64.StdEncoding.DecodeString(base64_encrypt_msg)
|
|
|
+ if nil != err {
|
|
|
+ return nil, NewCryptError(DecodeBase64Error, err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ block, err := aes.NewCipher(aeskey)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewCryptError(DecryptAESError, err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(encrypt_msg) < aes.BlockSize {
|
|
|
+ return nil, NewCryptError(DecryptAESError, "encrypt_msg size is not valid")
|
|
|
+ }
|
|
|
+
|
|
|
+ iv := aeskey[:aes.BlockSize]
|
|
|
+
|
|
|
+ if len(encrypt_msg)%aes.BlockSize != 0 {
|
|
|
+ return nil, NewCryptError(DecryptAESError, "encrypt_msg not a multiple of the block size")
|
|
|
+ }
|
|
|
+
|
|
|
+ mode := cipher.NewCBCDecrypter(block, iv)
|
|
|
+
|
|
|
+ mode.CryptBlocks(encrypt_msg, encrypt_msg)
|
|
|
+
|
|
|
+ return encrypt_msg, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) calSignature(timestamp, nonce, data string) string {
|
|
|
+ sort_arr := []string{self.token, timestamp, nonce, data}
|
|
|
+ sort.Strings(sort_arr);
|
|
|
+ var buffer bytes.Buffer
|
|
|
+ for _, value := range sort_arr {
|
|
|
+ buffer.WriteString(value)
|
|
|
+ }
|
|
|
+
|
|
|
+ sha := sha1.New()
|
|
|
+ sha.Write(buffer.Bytes())
|
|
|
+ signature := fmt.Sprintf("%x", sha.Sum(nil))
|
|
|
+ return string(signature)
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) ParsePlainText(plaintext []byte) ([]byte, uint32, []byte, []byte, *CryptError) {
|
|
|
+ const block_size = 32
|
|
|
+ plaintext, err := self.pKCS7Unpadding(plaintext, block_size)
|
|
|
+ if nil != err {
|
|
|
+ return nil, 0, nil, nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ text_len := uint32(len(plaintext))
|
|
|
+ if text_len < 20 {
|
|
|
+ return nil, 0, nil, nil, NewCryptError(IllegalBuffer, "plain is to small 1")
|
|
|
+ }
|
|
|
+ random := plaintext[:16]
|
|
|
+ msg_len := binary.BigEndian.Uint32(plaintext[16:20])
|
|
|
+ if text_len < (20 + msg_len) {
|
|
|
+ return nil, 0, nil, nil, NewCryptError(IllegalBuffer, "plain is to small 2")
|
|
|
+ }
|
|
|
+
|
|
|
+ msg := plaintext[20 : 20+msg_len]
|
|
|
+ receiver_id := plaintext[20+msg_len:]
|
|
|
+
|
|
|
+ return random, msg_len, msg, receiver_id, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) VerifyURL(msg_signature, timestamp, nonce, echostr string) ([]byte, *CryptError) {
|
|
|
+ signature := self.calSignature(timestamp, nonce, echostr)
|
|
|
+
|
|
|
+ if strings.Compare(signature, msg_signature) != 0 {
|
|
|
+ return nil, NewCryptError(ValidateSignatureError, "signature not equal")
|
|
|
+ }
|
|
|
+
|
|
|
+ plaintext, err := self.cbcDecrypter(echostr)
|
|
|
+ if nil != err {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ _, _, msg, receiver_id, err := self.ParsePlainText(plaintext)
|
|
|
+ if nil != err {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(self.receiver_id) > 0 && strings.Compare(string(receiver_id), self.receiver_id) != 0 {
|
|
|
+ fmt.Println(string(receiver_id), self.receiver_id, len(receiver_id), len(self.receiver_id))
|
|
|
+ return nil, NewCryptError(ValidateCorpidError, "receiver_id is not equil")
|
|
|
+ }
|
|
|
+
|
|
|
+ return msg, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) EncryptMsg(reply_msg, timestamp, nonce string) ([]byte, *CryptError) {
|
|
|
+ rand_str := self.randString(16)
|
|
|
+ var buffer bytes.Buffer
|
|
|
+ buffer.WriteString(rand_str)
|
|
|
+
|
|
|
+ msg_len_buf := make([]byte, 4)
|
|
|
+ binary.BigEndian.PutUint32(msg_len_buf, uint32(len(reply_msg)))
|
|
|
+ buffer.Write(msg_len_buf)
|
|
|
+ buffer.WriteString(reply_msg);
|
|
|
+ buffer.WriteString(self.receiver_id);
|
|
|
+
|
|
|
+ tmp_ciphertext, err := self.cbcEncrypter(buffer.String());
|
|
|
+ if nil != err {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ ciphertext := string(tmp_ciphertext)
|
|
|
+
|
|
|
+ signature := self.calSignature(timestamp, nonce, ciphertext)
|
|
|
+
|
|
|
+ msg4_send := NewWXBizJsonMsg4Send(ciphertext, signature, timestamp, nonce)
|
|
|
+ return self.protocol_processor.serialize(msg4_send)
|
|
|
+}
|
|
|
+
|
|
|
+func (self *WXBizMsgCrypt) DecryptMsg(msg_signature, timestamp, nonce string, post_data []byte) ([]byte, *CryptError) {
|
|
|
+ msg4_recv, crypt_err := self.protocol_processor.parse(post_data)
|
|
|
+ if nil != crypt_err {
|
|
|
+ return nil, crypt_err
|
|
|
+ }
|
|
|
+
|
|
|
+ signature := self.calSignature(timestamp, nonce, msg4_recv.Encrypt)
|
|
|
+
|
|
|
+ if strings.Compare(signature, msg_signature) != 0 {
|
|
|
+ return nil, NewCryptError(ValidateSignatureError, "signature not equal")
|
|
|
+ }
|
|
|
+
|
|
|
+ plaintext, crypt_err := self.cbcDecrypter(msg4_recv.Encrypt)
|
|
|
+ if nil != crypt_err {
|
|
|
+ return nil, crypt_err
|
|
|
+ }
|
|
|
+
|
|
|
+ _, _, msg, receiver_id, crypt_err := self.ParsePlainText(plaintext)
|
|
|
+ if nil != crypt_err {
|
|
|
+ return nil, crypt_err
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(self.receiver_id) > 0 && strings.Compare(string(receiver_id), self.receiver_id) != 0 {
|
|
|
+ return nil, NewCryptError(ValidateCorpidError, "receiver_id is not equil")
|
|
|
+ }
|
|
|
+
|
|
|
+ return msg, nil
|
|
|
+}
|
|
|
+
|