[add] yao.crypto.WeworkDecrypt
This commit is contained in:
parent
b96e519981
commit
fbd3fc4c3c
4 changed files with 343 additions and 0 deletions
|
|
@ -10,6 +10,8 @@ func init() {
|
||||||
gou.RegisterProcessHandler("yao.crypto.hmac", ProcessHmac) // deprecated → crypto.Hash
|
gou.RegisterProcessHandler("yao.crypto.hmac", ProcessHmac) // deprecated → crypto.Hash
|
||||||
gou.RegisterProcessHandler("yao.crypto.AESBase64Encode", processBase64AESEncode)
|
gou.RegisterProcessHandler("yao.crypto.AESBase64Encode", processBase64AESEncode)
|
||||||
gou.RegisterProcessHandler("yao.crypto.AESBase64Decode", processBase64AESDecode)
|
gou.RegisterProcessHandler("yao.crypto.AESBase64Decode", processBase64AESDecode)
|
||||||
|
gou.RegisterProcessHandler("yao.crypto.WeworkDecrypt", processWeworkDecrypt)
|
||||||
|
|
||||||
gou.AliasProcess("yao.crypto.hash", "crypto.Hash")
|
gou.AliasProcess("yao.crypto.hash", "crypto.Hash")
|
||||||
gou.AliasProcess("yao.crypto.hmac", "crypto.Hmac")
|
gou.AliasProcess("yao.crypto.hmac", "crypto.Hmac")
|
||||||
}
|
}
|
||||||
|
|
@ -83,3 +85,22 @@ func processBase64AESDecode(process *gou.Process) interface{} {
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func processWeworkDecrypt(process *gou.Process) interface{} {
|
||||||
|
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
encodingAESKey := process.ArgsString(0)
|
||||||
|
msgEncrypt := process.ArgsString(1)
|
||||||
|
parseXML := false
|
||||||
|
|
||||||
|
if process.NumOfArgsIs(3) {
|
||||||
|
parseXML = process.ArgsBool(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := WeworkDecrypt(encodingAESKey, msgEncrypt, parseXML)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("error: %s msgEncrypt: %s", 400, err, msgEncrypt).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
||||||
81
crypto/wework.go
Normal file
81
crypto/wework.go
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WeworkDecrypt wework msg Decrypt
|
||||||
|
func WeworkDecrypt(encodingAESKey string, msgEncrypt string, parse bool) (map[string]interface{}, error) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
aseKey, err := base64.StdEncoding.DecodeString(encodingAESKey + "=")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext, err := base64.StdEncoding.DecodeString(msgEncrypt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
randMsg, err := aesDecrypt(ciphertext, aseKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
content := randMsg[16:]
|
||||||
|
buf := bytes.NewBuffer(content[0:4])
|
||||||
|
var len int32
|
||||||
|
binary.Read(buf, binary.BigEndian, &len)
|
||||||
|
msg := content[4 : len+4]
|
||||||
|
receiveid := content[len+4:]
|
||||||
|
|
||||||
|
data := map[string]interface{}{}
|
||||||
|
if parse {
|
||||||
|
data, err = parseXML(string(msg))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{
|
||||||
|
"message": string(msg),
|
||||||
|
"data": data,
|
||||||
|
"receiveid": string(receiveid),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseXML(data string) (map[string]interface{}, error) {
|
||||||
|
|
||||||
|
decoder := NewDecoder(strings.NewReader(data))
|
||||||
|
result, err := decoder.Decode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func aesDecrypt(crypted, key []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockSize := block.BlockSize()
|
||||||
|
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||||
|
origData := make([]byte, len(crypted))
|
||||||
|
blockMode.CryptBlocks(origData, crypted)
|
||||||
|
origData = pckS5UnPadding(origData)
|
||||||
|
return origData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pckS5UnPadding(origData []byte) []byte {
|
||||||
|
length := len(origData)
|
||||||
|
unpadding := int(origData[length-1])
|
||||||
|
return origData[:(length - unpadding)]
|
||||||
|
}
|
||||||
61
crypto/wework_test.go
Normal file
61
crypto/wework_test.go
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/yaoapp/gou"
|
||||||
|
"github.com/yaoapp/kun/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWework(t *testing.T) {
|
||||||
|
|
||||||
|
msgEncrypt := "meqbMyPr58hNy0j0YDdG9UT60UJZSh/tb3KOZt3z2SCKr6uvmSLbEnUCM89iFXS0BLWn11FOrD/xXsGUlVUSBw=="
|
||||||
|
encodingAESKey := "RhH75tStMzrH8bMxkTw8BrBfr0ZWULL5himUaRWCs7H"
|
||||||
|
|
||||||
|
res, err := WeworkDecrypt(encodingAESKey, msgEncrypt, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "8446271472585838141", res["message"])
|
||||||
|
assert.Equal(t, "wwe146299c731e6301", res["receiveid"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWeworkProcess(t *testing.T) {
|
||||||
|
|
||||||
|
msgEncrypt := "meqbMyPr58hNy0j0YDdG9UT60UJZSh/tb3KOZt3z2SCKr6uvmSLbEnUCM89iFXS0BLWn11FOrD/xXsGUlVUSBw=="
|
||||||
|
encodingAESKey := "RhH75tStMzrH8bMxkTw8BrBfr0ZWULL5himUaRWCs7H"
|
||||||
|
|
||||||
|
args := []interface{}{encodingAESKey, msgEncrypt}
|
||||||
|
res := gou.NewProcess("yao.crypto.WeworkDecrypt", args...).Run().(map[string]interface{})
|
||||||
|
|
||||||
|
assert.Equal(t, "8446271472585838141", res["message"])
|
||||||
|
assert.Equal(t, "wwe146299c731e6301", res["receiveid"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWeworkParseXML(t *testing.T) {
|
||||||
|
|
||||||
|
xml := `
|
||||||
|
<xml>
|
||||||
|
<ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName>
|
||||||
|
<FromUserName><![CDATA[mycreate]]></FromUserName>
|
||||||
|
<CreateTime>1409659813</CreateTime>
|
||||||
|
<MsgType><![CDATA[text]]></MsgType>
|
||||||
|
<Content><![CDATA[hello]]></Content>
|
||||||
|
<MsgId>4561255354251345929</MsgId>
|
||||||
|
<AgentID>218</AgentID>
|
||||||
|
<Nest>
|
||||||
|
<Id>111</Id>
|
||||||
|
</Nest>
|
||||||
|
</xml>`
|
||||||
|
|
||||||
|
data, err := parseXML(xml)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := maps.Of(data).Dot()
|
||||||
|
assert.Equal(t, "218", res.Get("xml.AgentID"))
|
||||||
|
assert.Equal(t, "111", res.Get("xml.Nest.Id"))
|
||||||
|
}
|
||||||
180
crypto/xml.go
Normal file
180
crypto/xml.go
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
attrPrefix = "@"
|
||||||
|
textPrefix = "#text"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//ErrInvalidDocument invalid document err
|
||||||
|
ErrInvalidDocument = errors.New("invalid document")
|
||||||
|
|
||||||
|
//ErrInvalidRoot data at the root level is invalid err
|
||||||
|
ErrInvalidRoot = errors.New("data at the root level is invalid")
|
||||||
|
)
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
Parent *node
|
||||||
|
Value map[string]interface{}
|
||||||
|
Attrs []xml.Attr
|
||||||
|
Label string
|
||||||
|
Space string
|
||||||
|
Text string
|
||||||
|
HasMany bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder instance
|
||||||
|
type Decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
attrPrefix string
|
||||||
|
textPrefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder create new decoder instance
|
||||||
|
func NewDecoder(reader io.Reader) *Decoder {
|
||||||
|
return NewDecoderWithPrefix(reader, attrPrefix, textPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoderWithPrefix create new decoder instance with custom attribute prefix and text prefix
|
||||||
|
func NewDecoderWithPrefix(reader io.Reader, attrPrefix, textPrefix string) *Decoder {
|
||||||
|
return &Decoder{r: reader, attrPrefix: attrPrefix, textPrefix: textPrefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode xml string to map[string]interface{}
|
||||||
|
func (d *Decoder) Decode() (map[string]interface{}, error) {
|
||||||
|
decoder := xml.NewDecoder(d.r)
|
||||||
|
n := &node{}
|
||||||
|
stack := make([]*node, 0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
token, err := decoder.Token()
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if token == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tok := token.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
{
|
||||||
|
label := tok.Name.Local
|
||||||
|
if tok.Name.Space != "" {
|
||||||
|
label = fmt.Sprintf("%s:%s", strings.ToLower(path.Base(tok.Name.Space)), tok.Name.Local)
|
||||||
|
}
|
||||||
|
n = &node{
|
||||||
|
Label: label,
|
||||||
|
Space: tok.Name.Space,
|
||||||
|
Parent: n,
|
||||||
|
Value: map[string]interface{}{label: map[string]interface{}{}},
|
||||||
|
Attrs: tok.Attr,
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttrs(n, &tok, d.attrPrefix)
|
||||||
|
stack = append(stack, n)
|
||||||
|
|
||||||
|
if n.Parent != nil {
|
||||||
|
n.Parent.HasMany = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case xml.CharData:
|
||||||
|
data := strings.TrimSpace(string(tok))
|
||||||
|
if len(stack) > 0 {
|
||||||
|
stack[len(stack)-1].Text = data
|
||||||
|
} else if len(data) > 0 {
|
||||||
|
return nil, ErrInvalidRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
case xml.EndElement:
|
||||||
|
{
|
||||||
|
length := len(stack)
|
||||||
|
stack, n = stack[:length-1], stack[length-1]
|
||||||
|
|
||||||
|
if !n.HasMany {
|
||||||
|
if len(n.Attrs) > 0 {
|
||||||
|
m := n.Value[n.Label].(map[string]interface{})
|
||||||
|
m[d.textPrefix] = n.Text
|
||||||
|
} else {
|
||||||
|
n.Value[n.Label] = n.Text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stack) == 0 {
|
||||||
|
return n.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
setNodeValue(n)
|
||||||
|
n = n.Parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrInvalidDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
func setAttrs(n *node, tok *xml.StartElement, attrPrefix string) {
|
||||||
|
if len(tok.Attr) > 0 {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
for _, attr := range tok.Attr {
|
||||||
|
if len(attr.Name.Space) > 0 {
|
||||||
|
m[attrPrefix+attr.Name.Space+":"+attr.Name.Local] = attr.Value
|
||||||
|
} else {
|
||||||
|
m[attrPrefix+attr.Name.Local] = attr.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.Value[tok.Name.Local] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNodeValue(n *node) {
|
||||||
|
if v, ok := n.Parent.Value[n.Parent.Label]; ok {
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
if v, ok = m[n.Label]; ok {
|
||||||
|
switch item := v.(type) {
|
||||||
|
case string:
|
||||||
|
m[n.Label] = []string{item, n.Value[n.Label].(string)}
|
||||||
|
case []string:
|
||||||
|
m[n.Label] = append(item, n.Value[n.Label].(string))
|
||||||
|
case map[string]interface{}:
|
||||||
|
vm := getMap(n)
|
||||||
|
if vm != nil {
|
||||||
|
m[n.Label] = []map[string]interface{}{item, vm}
|
||||||
|
}
|
||||||
|
case []map[string]interface{}:
|
||||||
|
vm := getMap(n)
|
||||||
|
if vm != nil {
|
||||||
|
m[n.Label] = append(item, vm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m[n.Label] = n.Value[n.Label]
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
n.Parent.Value[n.Parent.Label] = n.Value[n.Label]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMap(node *node) map[string]interface{} {
|
||||||
|
if v, ok := node.Value[node.Label]; ok {
|
||||||
|
switch v.(type) {
|
||||||
|
case string:
|
||||||
|
return map[string]interface{}{node.Label: v}
|
||||||
|
case map[string]interface{}:
|
||||||
|
return node.Value[node.Label].(map[string]interface{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue