From 803240fc9f70d4a3889c37f93c4f7c875851bf22 Mon Sep 17 00:00:00 2001 From: gazebo Date: Sat, 9 Mar 2019 08:10:04 +0800 Subject: [PATCH] - dingding callback --- platformapi/dingdingapi/callback.go | 81 ++++++++++++++++++++---- platformapi/dingdingapi/callback_test.go | 16 +++++ platformapi/dingdingapi/dingdingapi.go | 3 + platformapi/weixinapi/sns.go | 22 +++---- utils/utils_crypt.go | 43 +++++++++++++ utils/utils_crypt_test.go | 21 ++++++ 6 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 platformapi/dingdingapi/callback_test.go create mode 100644 utils/utils_crypt.go create mode 100644 utils/utils_crypt_test.go diff --git a/platformapi/dingdingapi/callback.go b/platformapi/dingdingapi/callback.go index b9d7d33e..506bc914 100644 --- a/platformapi/dingdingapi/callback.go +++ b/platformapi/dingdingapi/callback.go @@ -1,28 +1,81 @@ package dingdingapi +import ( + "crypto/sha1" + "encoding/base64" + "fmt" + "sort" + "strings" + "time" + + "git.rosy.net.cn/baseapi/utils" +) + const ( CBTagUserAddOrg = "user_add_org" CBTagUserModifyOrg = "user_modify_org" CBTagUserLeaveOrg = "user_leave_org" ) -func (a *API) encrypt(msg, aesKey string) (encryptedMsg string, err error) { - // binAesKey, err := base64.StdEncoding.DecodeString(aesKey) - // if err == nil { - // block, err2 := aes.NewCipher(binAesKey) - // if err = err2; err == nil { - // block.Encrypt() - // } - // } +const ( + KeyMsgSignature = "msg_signature" +) + +func (a *API) PackCallbackResult(result string) (resultMap map[string]interface{}) { + encryptedResult, err := a.Encrypt(result) + if err == nil { + resultMap = map[string]interface{}{ + "encrypt": encryptedResult, + "timeStamp": utils.Int64ToStr(time.Now().Unix()), + "nonce": utils.GetUUID(), + } + resultMap["msg_signature"] = a.calculateCallbackSign(resultMap) + } + return resultMap +} + +func (a *API) calculateCallbackSign(data map[string]interface{}) (sign string) { + strList := []string{ + a.callbackToken, + } + for k, v := range data { + if k != KeyMsgSignature { + strList = append(strList, v.(string)) + } + } + sort.Sort(sort.StringSlice(strList)) + return fmt.Sprintf("%x", sha1.Sum([]byte(strings.Join(strList, "")))) +} + +func (a *API) Encrypt(msg string) (encryptedMsg string, err error) { + binResult, err := utils.AESCBCEncpryt([]byte(msg), a.callbackAesKey, a.callbackAesKey[:16]) + encryptedMsg = base64.StdEncoding.EncodeToString(binResult) return encryptedMsg, err } +func (a *API) Decrypt(msg string) (decryptedMsg string, err error) { + binMsg, err := base64.StdEncoding.DecodeString(msg) + if err == nil { + binResult, err2 := utils.AESCBCDecpryt(binMsg, a.callbackAesKey, a.callbackAesKey[:16]) + if err = err2; err == nil { + decryptedMsg = string(binResult) + } + } + return decryptedMsg, err +} + func (a *API) RegisterCallback(callbackTagList []string, token, aesKey, urlStr string) (err error) { - _, err = a.AccessAPI("call_back/register_call_back", nil, map[string]interface{}{ - "call_back_tag": callbackTagList, - "token": token, - "aes_key": aesKey, - "url": urlStr, - }) + if len(callbackTagList) > 0 { // 为0做测试用 + _, err = a.AccessAPI("call_back/register_call_back", nil, map[string]interface{}{ + "call_back_tag": callbackTagList, + "token": token, + "aes_key": aesKey, + "url": urlStr, + }) + } + if err == nil { + a.callbackToken = token + a.callbackAesKey, _ = base64.StdEncoding.DecodeString(aesKey + "=") + } return err } diff --git a/platformapi/dingdingapi/callback_test.go b/platformapi/dingdingapi/callback_test.go new file mode 100644 index 00000000..35486b5c --- /dev/null +++ b/platformapi/dingdingapi/callback_test.go @@ -0,0 +1,16 @@ +package dingdingapi + +import "testing" + +func TestEncrypt(t *testing.T) { + api.RegisterCallback(nil, "token", "M3Z1b1FIXjlAWW84bEVxNENHSlZOUFJEbkAlRUZQXnE", "") + encryptedMsg, err := api.Encrypt("hello") + decryptedMsg, err := api.Decrypt(encryptedMsg) + t.Log(encryptedMsg, decryptedMsg, err) +} + +func TestPackCallbackResult(t *testing.T) { + api.RegisterCallback(nil, "token", "M3Z1b1FIXjlAWW84bEVxNENHSlZOUFJEbkAlRUZQXnE", "") + success := api.PackCallbackResult("success") + t.Log(success) +} diff --git a/platformapi/dingdingapi/dingdingapi.go b/platformapi/dingdingapi/dingdingapi.go index a151278b..a4909a78 100644 --- a/platformapi/dingdingapi/dingdingapi.go +++ b/platformapi/dingdingapi/dingdingapi.go @@ -34,6 +34,9 @@ type API struct { client *http.Client config *platformapi.APIConfig locker sync.RWMutex + + callbackToken string + callbackAesKey []byte } func New(appID, secret string, config ...*platformapi.APIConfig) *API { diff --git a/platformapi/weixinapi/sns.go b/platformapi/weixinapi/sns.go index 7253af08..19f8bc5d 100644 --- a/platformapi/weixinapi/sns.go +++ b/platformapi/weixinapi/sns.go @@ -1,8 +1,7 @@ package weixinapi import ( - "crypto/aes" - "crypto/cipher" + "bytes" "git.rosy.net.cn/baseapi/utils" ) @@ -86,19 +85,18 @@ func (a *API) SNSDecodeMiniProgramData(encryptedData, sessionKey, iv string) (de if err != nil { return nil, err } - c, err := aes.NewCipher(decodedDataList[1]) - if err != nil { - return nil, err - } - cfbdec := cipher.NewCBCDecrypter(c, decodedDataList[2][:c.BlockSize()]) - decryptedData = make([]byte, len(decodedDataList[0])) - cfbdec.CryptBlocks(decryptedData, decodedDataList[0]) - decryptedData = PKCS7UnPadding(decryptedData) - return decryptedData, nil + decryptedData, err = utils.AESCBCDecpryt(decodedDataList[0], decodedDataList[1], decodedDataList[2]) + return decryptedData, err } -func PKCS7UnPadding(origData []byte) []byte { +func PKCSUnPadding(origData []byte) []byte { length := len(origData) unpadding := int(origData[length-1]) return origData[:(length - unpadding)] } + +func PKCSPadding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} diff --git a/utils/utils_crypt.go b/utils/utils_crypt.go new file mode 100644 index 00000000..4e3aff7d --- /dev/null +++ b/utils/utils_crypt.go @@ -0,0 +1,43 @@ +package utils + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" +) + +func AESCBCEncpryt(data, aesKey, iv []byte) (encryptedData []byte, err error) { + c, err := aes.NewCipher(aesKey) + if err != nil { + return nil, err + } + cfbdec := cipher.NewCBCEncrypter(c, iv[:c.BlockSize()]) + data = PKCSPadding(data, c.BlockSize()) + encryptedData = make([]byte, len(data)) + cfbdec.CryptBlocks(encryptedData, data) + return encryptedData, nil +} + +func AESCBCDecpryt(encryptedData, aesKey, iv []byte) (decryptedData []byte, err error) { + c, err := aes.NewCipher(aesKey) + if err != nil { + return nil, err + } + cfbdec := cipher.NewCBCDecrypter(c, iv[:c.BlockSize()]) + decryptedData = make([]byte, len(encryptedData)) + cfbdec.CryptBlocks(decryptedData, encryptedData) + decryptedData = PKCSUnPadding(decryptedData) + return decryptedData, nil +} + +func PKCSUnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +func PKCSPadding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} diff --git a/utils/utils_crypt_test.go b/utils/utils_crypt_test.go new file mode 100644 index 00000000..abe0ac62 --- /dev/null +++ b/utils/utils_crypt_test.go @@ -0,0 +1,21 @@ +package utils + +import ( + "testing" +) + +func TestCrypt(t *testing.T) { + aesKey := []byte("123456789012345678901234567890ab") + msg := "hellasfsafsdsads" + encryptedMsg, err := AESCBCEncpryt([]byte(msg), aesKey, aesKey[:16]) + if err != nil { + t.Fatal(err) + } + decryptedMsg, err := AESCBCDecpryt(encryptedMsg, aesKey, aesKey[:16]) + if err != nil { + t.Fatal(err) + } + if msg != string(decryptedMsg) { + t.Fatal("result is wrong") + } +}