| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package crypto
- import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "crypto/sha1"
- "encoding/base64"
- "encoding/binary"
- "errors"
- "fmt"
- r "math/rand"
- "sort"
- "strings"
- "time"
- )
- type DingTalkCrypto struct {
- Token string
- EncodingAESKey string
- SuiteKey string
- BKey []byte
- Block cipher.Block
- }
- func NewDingTalkCrypto(token, encodingAESKey, suiteKey string) *DingTalkCrypto {
- fmt.Println(len(encodingAESKey))
- if len(encodingAESKey) != int(43) {
- panic("不合法的EncodingAESKey")
- }
- bkey, err := base64.StdEncoding.DecodeString(encodingAESKey + "=")
- if err != nil {
- panic(err.Error())
- }
- block, err := aes.NewCipher(bkey)
- if err != nil {
- panic(err.Error())
- }
- c := &DingTalkCrypto{
- Token: token,
- EncodingAESKey: encodingAESKey,
- SuiteKey: suiteKey,
- BKey: bkey,
- Block: block,
- }
- return c
- }
- func (c *DingTalkCrypto) GetDecryptMsg(signature, timestamp, nonce, secretMsg string) (string, error) {
- if !c.VerificationSignature(c.Token, timestamp, nonce, secretMsg, signature) {
- return "", errors.New("ERROR: 签名不匹配")
- }
- decode, err := base64.StdEncoding.DecodeString(secretMsg)
- if err != nil {
- return "", err
- }
- if len(decode) < aes.BlockSize {
- return "", errors.New("ERROR: 密文太短")
- }
- blockMode := cipher.NewCBCDecrypter(c.Block, c.BKey[:c.Block.BlockSize()])
- plantText := make([]byte, len(decode))
- blockMode.CryptBlocks(plantText, decode)
- plantText = pkCS7UnPadding(plantText)
- size := binary.BigEndian.Uint32(plantText[16:20])
- plantText = plantText[20:]
- corpID := plantText[size:]
- if string(corpID) != c.SuiteKey {
- return "", errors.New("ERROR: CorpID匹配不正确")
- }
- return string(plantText[:size]), nil
- }
- func (c *DingTalkCrypto) GetEncryptMsg(msg string) (map[string]string, error) {
- var timestamp = time.Now().UnixMilli()
- var nonce = RandomString(12)
- str, sign, err := c.GetEncryptMsgDetail(msg, fmt.Sprint(timestamp), nonce)
- return map[string]string{"nonce": nonce, "timeStamp": fmt.Sprint(timestamp), "encrypt": str, "msg_signature": sign}, err
- }
- func (c *DingTalkCrypto) GetEncryptMsgDetail(msg, timestamp, nonce string) (string, string, error) {
- size := make([]byte, 4)
- binary.BigEndian.PutUint32(size, uint32(len(msg)))
- msg = RandomString(16) + string(size) + msg + c.SuiteKey
- plantText := pkCS7Padding([]byte(msg), c.Block.BlockSize())
- if len(plantText)%aes.BlockSize != 0 {
- return "", "", errors.New("ERROR: 消息体size不为16的倍数")
- }
- blockMode := cipher.NewCBCEncrypter(c.Block, c.BKey[:c.Block.BlockSize()])
- chipherText := make([]byte, len(plantText))
- blockMode.CryptBlocks(chipherText, plantText)
- outMsg := base64.StdEncoding.EncodeToString(chipherText)
- signature := c.CreateSignature(c.Token, timestamp, nonce, string(outMsg))
- return string(outMsg), signature, nil
- }
- func sha1Sign(s string) string {
- // The pattern for generating a hash is `sha1.New()`,
- // `sha1.Write(bytes)`, then `sha1.Sum([]byte{})`.
- // Here we start with a new hash.
- h := sha1.New()
- // `Write` expects bytes. If you have a string `s`,
- // use `[]byte(s)` to coerce it to bytes.
- h.Write([]byte(s))
- // This gets the finalized hash result as a byte
- // slice. The argument to `Sum` can be used to append
- // to an existing byte slice: it usually isn't needed.
- bs := h.Sum(nil)
- // SHA1 values are often printed in hex, for example
- // in git commits. Use the `%x` format verb to convert
- // a hash results to a hex string.
- return fmt.Sprintf("%x", bs)
- }
- // CreateSignature 数据签名
- func (c *DingTalkCrypto) CreateSignature(token, timestamp, nonce, msg string) string {
- params := make([]string, 0)
- params = append(params, token)
- params = append(params, timestamp)
- params = append(params, nonce)
- params = append(params, msg)
- sort.Strings(params)
- return sha1Sign(strings.Join(params, ""))
- }
- // VerificationSignature 验证数据签名
- func (c *DingTalkCrypto) VerificationSignature(token, timestamp, nonce, msg, sigture string) bool {
- return c.CreateSignature(token, timestamp, nonce, msg) == sigture
- }
- // 解密补位
- func pkCS7UnPadding(plantText []byte) []byte {
- length := len(plantText)
- unpadding := int(plantText[length-1])
- return plantText[:(length - unpadding)]
- }
- // 加密补位
- func pkCS7Padding(ciphertext []byte, blockSize int) []byte {
- padding := blockSize - len(ciphertext)%blockSize
- padtext := bytes.Repeat([]byte{byte(padding)}, padding)
- return append(ciphertext, padtext...)
- }
- // 随机字符串
- func RandomString(n int, alphabets ...byte) string {
- const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- var bytes = make([]byte, n)
- var randby bool
- if num, err := rand.Read(bytes); num != n || err != nil {
- r.Seed(time.Now().UnixNano())
- randby = true
- }
- for i, b := range bytes {
- if len(alphabets) == 0 {
- if randby {
- bytes[i] = alphanum[r.Intn(len(alphanum))]
- } else {
- bytes[i] = alphanum[b%byte(len(alphanum))]
- }
- } else {
- if randby {
- bytes[i] = alphabets[r.Intn(len(alphabets))]
- } else {
- bytes[i] = alphabets[b%byte(len(alphabets))]
- }
- }
- }
- return string(bytes)
- }
|