添加企业微信解密方式
This commit is contained in:
@@ -15,10 +15,6 @@ type SuiteTicketInfo struct {
|
||||
SuiteTicket string `xml:"SuiteTicket"` // Ticket内容,最长为512字节
|
||||
}
|
||||
|
||||
type SuiteTicketXml struct {
|
||||
Xml *SuiteTicketInfo `xml:"xml"`
|
||||
}
|
||||
|
||||
func (a *API) GetEnterpriseMsg(request *http.Request) (*SuiteTicketInfo, error) {
|
||||
data, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
|
||||
115
utils/weworkapi_golang-master/httpserver.go
Normal file
115
utils/weworkapi_golang-master/httpserver.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package weworkapi_golang
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const token = "nn3P9sfkGK5UlHgtNoAqB"
|
||||
const receiverId = "ww9a156bfa070e1857"
|
||||
const encodingAeskey = "471RkJkfISWJQUC2f8DSdOgXH0reVCxWCpfaTawSIWA"
|
||||
|
||||
func getString(str, endstr string, start int, msg *string) int {
|
||||
end := strings.Index(str, endstr)
|
||||
*msg = str[start:end]
|
||||
return end + len(endstr)
|
||||
}
|
||||
|
||||
func VerifyURL(w http.ResponseWriter, r *http.Request) {
|
||||
//httpstr := `&{GET /?msg_signature=825075c093249d5a60967fe4a613cae93146636b×tamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D HTTP/1.1 1 1 map[Cache-Control:[no-cache] Accept:[*/*] Pragma:[no-cache] User-Agent:[Mozilla/4.0]] 0x86c180 0 [] false 100.108.211.112:8893 map[] map[] <nil> map[] 100.108.79.233:59663 /?msg_signature=825075c093249d5a60967fe4a613cae93146636b×tamp=1597998748&nonce=1597483820&echostr=neLB8CftccHiz19tluVb%2BUBnUVMT3xpUMZU8qvDdD17eH8XfEsbPYC%2FkJyPsZOOc6GdsCeu8jSIa2noSJ%2Fez2w%3D%3D <nil>}`
|
||||
fmt.Println(r, r.Body)
|
||||
httpstr := r.URL.RawQuery
|
||||
start := strings.Index(httpstr, "msg_signature=")
|
||||
start += len("msg_signature=")
|
||||
|
||||
var msg_signature string
|
||||
next := getString(httpstr, "×tamp=", start, &msg_signature)
|
||||
|
||||
var timestamp string
|
||||
next = getString(httpstr, "&nonce=", next, ×tamp)
|
||||
|
||||
var nonce string
|
||||
next = getString(httpstr, "&echostr=", next, &nonce)
|
||||
|
||||
echostr := httpstr[next:len(httpstr)]
|
||||
|
||||
echostr, _ = url.QueryUnescape(echostr)
|
||||
fmt.Println(msg_signature, timestamp, nonce, echostr, next)
|
||||
|
||||
wxcpt := NewWXBizMsgCrypt(token, encodingAeskey, receiverId, JsonType)
|
||||
echoStr, cryptErr := wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr)
|
||||
if nil != cryptErr {
|
||||
fmt.Println("verifyUrl fail", cryptErr)
|
||||
}
|
||||
fmt.Println("verifyUrl success echoStr", string(echoStr))
|
||||
fmt.Fprintf(w, string(echoStr))
|
||||
|
||||
}
|
||||
|
||||
func MsgHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpstr := r.URL.RawQuery
|
||||
start := strings.Index(httpstr, "msg_signature=")
|
||||
start += len("msg_signature=")
|
||||
|
||||
var msg_signature string
|
||||
next := getString(httpstr, "×tamp=", start, &msg_signature)
|
||||
|
||||
var timestamp string
|
||||
next = getString(httpstr, "&nonce=", next, ×tamp)
|
||||
|
||||
nonce := httpstr[next:len(httpstr)]
|
||||
fmt.Println(msg_signature, timestamp, nonce)
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
fmt.Println(string(body), err)
|
||||
wxcpt := NewWXBizMsgCrypt(token, encodingAeskey, receiverId, JsonType)
|
||||
|
||||
msg, err_ := wxcpt.DecryptMsg(msg_signature, timestamp, nonce, body)
|
||||
fmt.Println(string(msg), err_)
|
||||
var msgContent MsgContent
|
||||
err = json.Unmarshal(msg, &msgContent)
|
||||
if nil != err {
|
||||
fmt.Println("Unmarshal fail", err)
|
||||
} else {
|
||||
fmt.Println("struct", msgContent)
|
||||
}
|
||||
|
||||
fmt.Println(msgContent, err)
|
||||
ToUsername := msgContent.ToUsername
|
||||
msgContent.ToUsername = msgContent.FromUsername
|
||||
msgContent.FromUsername = ToUsername
|
||||
fmt.Println("replaymsg", msgContent)
|
||||
replayJson, err := json.Marshal(&msgContent)
|
||||
|
||||
encryptMsg, cryptErr := wxcpt.EncryptMsg(string(replayJson), "1409659589", "1409659589")
|
||||
if nil != cryptErr {
|
||||
fmt.Println("DecryptMsg fail", cryptErr)
|
||||
}
|
||||
|
||||
sEncryptMsg := string(encryptMsg)
|
||||
|
||||
fmt.Println("after encrypt sEncryptMsg: ", sEncryptMsg)
|
||||
fmt.Fprintf(w, sEncryptMsg)
|
||||
}
|
||||
|
||||
func CallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpstr := r.URL.RawQuery
|
||||
echo := strings.Index(httpstr, "echostr")
|
||||
if echo != -1 {
|
||||
VerifyURL(w, r)
|
||||
} else {
|
||||
MsgHandler(w, r)
|
||||
}
|
||||
|
||||
fmt.Println("finished CallbackHandler", httpstr)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", CallbackHandler) // 设置访问路由
|
||||
log.Fatal(http.ListenAndServe(":8893", nil))
|
||||
}
|
||||
139
utils/weworkapi_golang-master/sample.go
Normal file
139
utils/weworkapi_golang-master/sample.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package weworkapi_golang
|
||||
|
||||
//
|
||||
//import (
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
//)
|
||||
//
|
||||
type MsgContent struct {
|
||||
ToUsername string `json:"ToUserName"`
|
||||
FromUsername string `json:"FromUserName"`
|
||||
CreateTime uint32 `json:"CreateTime"`
|
||||
MsgType string `json:"MsgType"`
|
||||
Content string `json:"Content"`
|
||||
Msgid uint64 `json:"MsgId"`
|
||||
Agentid uint32 `json:"AgentId"`
|
||||
}
|
||||
|
||||
//func main() {
|
||||
// token := "QDG6eK"
|
||||
// receiverId := "wx5823bf96d3bd56c7"
|
||||
// encodingAeskey := "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C"
|
||||
// wxcpt := NewWXBizMsgCrypt(token, encodingAeskey, receiverId, JsonType)
|
||||
// /*
|
||||
// ------------使用示例一:验证回调URL---------------
|
||||
// *企业开启回调模式时,企业微信会向验证url发送一个get请求
|
||||
// 假设点击验证时,企业收到类似请求:
|
||||
// * GET /cgi-bin/wxpush?msg_signature=5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3×tamp=1409659589&nonce=263014780&echostr=P9nAzCzyDtyTWESHep1vC5X9xho%2FqYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp%2B4RPcs8TgAE7OaBO%2BFZXvnaqQ%3D%3D
|
||||
// * HTTP/1.1 Host: qy.weixin.qq.com
|
||||
//
|
||||
// 接收到该请求时,企业应
|
||||
// 1.解析出Get请求的参数,包括消息体签名(msg_signature),时间戳(timestamp),随机数字串(nonce)以及企业微信推送过来的随机加密字符串(echostr),
|
||||
// 这一步注意作URL解码。
|
||||
// 2.验证消息体签名的正确性
|
||||
// 3. 解密出echostr原文,将原文当作Get请求的response,返回给企业微信
|
||||
// 第2,3步可以用企业微信提供的库函数VerifyURL来实现。
|
||||
//
|
||||
// */
|
||||
// // 解析出url上的参数值如下:
|
||||
// // verifyMsgSign := HttpUtils.ParseUrl("msg_signature")
|
||||
// verifyMsgSign := "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3"
|
||||
// // verifyTimestamp := HttpUtils.ParseUrl("timestamp")
|
||||
// verifyTimestamp := "1409659589"
|
||||
// // verifyNonce := HttpUtils.ParseUrl("nonce")
|
||||
// verifyNonce := "263014780"
|
||||
// // verifyEchoStr := HttpUtils.ParseUrl("echoStr")
|
||||
// verifyEchoStr := "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ=="
|
||||
// echoStr, cryptErr := wxcpt.VerifyURL(verifyMsgSign, verifyTimestamp, verifyNonce, verifyEchoStr)
|
||||
// if nil != cryptErr {
|
||||
// fmt.Println("verifyUrl fail", cryptErr)
|
||||
// }
|
||||
// fmt.Println("verifyUrl success echoStr", string(echoStr))
|
||||
// // 验证URL成功,将sEchoStr返回
|
||||
// // HttpUtils.SetResponse(sEchoStr)
|
||||
//
|
||||
// /*
|
||||
// ------------使用示例二:对用户回复的消息解密---------------
|
||||
// 用户回复消息或者点击事件响应时,企业会收到回调消息,此消息是经过企业微信加密之后的密文以post形式发送给企业,密文格式请参考官方文档
|
||||
// 假设企业收到企业微信的回调消息如下:
|
||||
// POST /cgi-bin/wxpush? msg_signature=477715d11cdb4164915debcba66cb864d751f3e6×tamp=1409659813&nonce=1372623149 HTTP/1.1
|
||||
// Host: qy.weixin.qq.com
|
||||
// Content-Length: 613
|
||||
// {
|
||||
// "tousername":"wx5823bf96d3bd56c7",
|
||||
// "encrypt":"CZWs4CWRpI4VolQlvn4dlPBlXke6+HgmuI7p0LueFp1fKH40TNL+YHWJZwqIiYV+3kTrhdNU7fZwc+PmtgBvxSczkFeRz+oaVSsomrrtP2Z91LE313djjbWujqInRT+7ChGbCeo7ZzszByf8xnDSunPBxRX1MfX3kAxpKq7dqduW1kpMAx8O8xUzZ9oC0TLuZchbpxaml4epzGfF21O+zyXDwTxbCEiO0E87mChtzuh/VPlznXYbfqVrnyLNZ5pr",
|
||||
// "agentid":"218"
|
||||
// }
|
||||
//
|
||||
// 企业收到post请求之后应该:
|
||||
// 1.解析出url上的参数,包括消息体签名(msg_signature),时间戳(timestamp)以及随机数字串(nonce)
|
||||
// 2.验证消息体签名的正确性。
|
||||
// 3.将post请求的数据进行json解析,并将"Encrypt"标签的内容进行解密,解密出来的明文即是用户回复消息的明文,明文格式请参考官方文档
|
||||
// 第2,3步可以用企业微信提供的库函数DecryptMsg来实现。
|
||||
// */
|
||||
//
|
||||
// // reqMsgSign := HttpUtils.ParseUrl("msg_signature")
|
||||
// reqMsgSign := "0623cbc5a8cbee5bcc137c70de99575366fc2af3"
|
||||
// // reqTimestamp := HttpUtils.ParseUrl("timestamp")
|
||||
// reqTimestamp := "1409659813"
|
||||
// // reqNonce := HttpUtils.ParseUrl("nonce")
|
||||
// reqNonce := "1372623149"
|
||||
// // post请求的密文数据
|
||||
// // reqData = HttpUtils.PostData()
|
||||
//
|
||||
// reqData := []byte(`{"tousername":"wx5823bf96d3bd56c7","encrypt":"CZWs4CWRpI4VolQlvn4dlEC1alN2MUEY2VklGehgBVLBrlVF7SyT+SV+Toj43l4ayJ9UMGKphktKKmP7B2j/P1ey67XB8PBgS7Wr5/8+w/yWriZv3Vmoo/MH3/1HsIWZrPQ3N2mJrelStIfI2Y8kLKXA7EhfZgZX4o+ffdkZDM76SEl79Ib9mw7TGjZ9Aw/x/A2VjNbV1E8BtEbRxYYcQippYNw7hr8sFfa3nW1xLdxokt8QkRX83vK3DFP2F6TQFPL2Tu98UwhcUpPvdJBuu1/yiOQIScppV3eOuLWEsko=","agentid":"218"}`)
|
||||
//
|
||||
// msg, cryptErr := wxcpt.DecryptMsg(reqMsgSign, reqTimestamp, reqNonce, reqData)
|
||||
// if nil != cryptErr {
|
||||
// fmt.Println("DecryptMsg fail", cryptErr)
|
||||
// }
|
||||
// fmt.Println("after decrypt msg: ", string(msg))
|
||||
// // TODO: 解析出明文json标签的内容进行处理
|
||||
// // For example:
|
||||
//
|
||||
// var msgContent MsgContent
|
||||
// err := json.Unmarshal(msg, &msgContent)
|
||||
// if nil != err {
|
||||
// fmt.Println("Unmarshal fail", err)
|
||||
// } else {
|
||||
// fmt.Println("struct", msgContent)
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// ------------使用示例三:企业回复用户消息的加密---------------
|
||||
// 企业被动回复用户的消息也需要进行加密,并且拼接成密文格式的json串。
|
||||
// 假设企业需要回复用户的明文如下:
|
||||
//
|
||||
// {
|
||||
// "ToUserName": "mycreate",
|
||||
// "FromUserName":"wx5823bf96d3bd56c7",
|
||||
// "CreateTime": 1348831860,
|
||||
// "MsgType": "text",
|
||||
// "Content": "this is a test",
|
||||
// "MsgId": 1234567890123456,
|
||||
// "AgentID": 128
|
||||
// }
|
||||
//
|
||||
// 为了将此段明文回复给用户,企业应:
|
||||
// 1.自己生成时间时间戳(timestamp),随机数字串(nonce)以便生成消息体签名,也可以直接用从企业微信的post url上解析出的对应值。
|
||||
// 2.将明文加密得到密文。
|
||||
// 3.用密文,步骤1生成的timestamp,nonce和企业在企业微信设定的token生成消息体签名。
|
||||
// 4.将密文,消息体签名,时间戳,随机数字串拼接成json格式的字符串,发送给企业。
|
||||
// 以上2,3,4步可以用企业微信提供的库函数EncryptMsg来实现。
|
||||
// */
|
||||
// respData := "{\"ToUserName\":\"wx5823bf96d3bd56c7\",\"FromUserName\":\"mycreate\",\"CreateTime\": 1409659813,\"MsgType\":\"text\",\"Content\":\"hello\",\"MsgId\":4561255354251345929,\"AgentID\": 218}"
|
||||
// //respData := `{"ToUserName":"wx5823bf96d3bd56c7","FromUserName":"mycreate","CreateTime": 1409659813,"MsgType":"text","Content":"hello","MsgId":4561255354251345929,"AgentID": 218}`
|
||||
// //respData := `{"FromUserName":"mycreate","CreateTime": 1409659813,"MsgType":"text","Content":"hello","MsgId":4561255354251345929,"AgentID": 218}`
|
||||
// encryptMsg, cryptErr := wxcpt.EncryptMsg(respData, reqTimestamp, reqNonce)
|
||||
// if nil != cryptErr {
|
||||
// fmt.Println("DecryptMsg fail", cryptErr)
|
||||
// }
|
||||
//
|
||||
// sEncryptMsg := string(encryptMsg)
|
||||
//
|
||||
// fmt.Println("after encrypt sEncryptMsg: ", sEncryptMsg)
|
||||
// // 加密成功
|
||||
// // TODO:
|
||||
// // HttpUtils.SetResponse(sEncryptMsg)
|
||||
//}
|
||||
310
utils/weworkapi_golang-master/wxbizjsonmsgcrypt.go
Normal file
310
utils/weworkapi_golang-master/wxbizjsonmsgcrypt.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package weworkapi_golang
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user