crypto.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package crypto
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/rand"
  7. "crypto/sha1"
  8. "encoding/base64"
  9. "encoding/binary"
  10. "errors"
  11. "fmt"
  12. r "math/rand"
  13. "sort"
  14. "strings"
  15. "time"
  16. )
  17. type DingTalkCrypto struct {
  18. Token string
  19. EncodingAESKey string
  20. SuiteKey string
  21. BKey []byte
  22. Block cipher.Block
  23. }
  24. func NewDingTalkCrypto(token, encodingAESKey, suiteKey string) *DingTalkCrypto {
  25. fmt.Println(len(encodingAESKey))
  26. if len(encodingAESKey) != int(43) {
  27. panic("不合法的EncodingAESKey")
  28. }
  29. bkey, err := base64.StdEncoding.DecodeString(encodingAESKey + "=")
  30. if err != nil {
  31. panic(err.Error())
  32. }
  33. block, err := aes.NewCipher(bkey)
  34. if err != nil {
  35. panic(err.Error())
  36. }
  37. c := &DingTalkCrypto{
  38. Token: token,
  39. EncodingAESKey: encodingAESKey,
  40. SuiteKey: suiteKey,
  41. BKey: bkey,
  42. Block: block,
  43. }
  44. return c
  45. }
  46. func (c *DingTalkCrypto) GetDecryptMsg(signature, timestamp, nonce, secretMsg string) (string, error) {
  47. if !c.VerificationSignature(c.Token, timestamp, nonce, secretMsg, signature) {
  48. return "", errors.New("ERROR: 签名不匹配")
  49. }
  50. decode, err := base64.StdEncoding.DecodeString(secretMsg)
  51. if err != nil {
  52. return "", err
  53. }
  54. if len(decode) < aes.BlockSize {
  55. return "", errors.New("ERROR: 密文太短")
  56. }
  57. blockMode := cipher.NewCBCDecrypter(c.Block, c.BKey[:c.Block.BlockSize()])
  58. plantText := make([]byte, len(decode))
  59. blockMode.CryptBlocks(plantText, decode)
  60. plantText = pkCS7UnPadding(plantText)
  61. size := binary.BigEndian.Uint32(plantText[16:20])
  62. plantText = plantText[20:]
  63. corpID := plantText[size:]
  64. if string(corpID) != c.SuiteKey {
  65. return "", errors.New("ERROR: CorpID匹配不正确")
  66. }
  67. return string(plantText[:size]), nil
  68. }
  69. func (c *DingTalkCrypto) GetEncryptMsg(msg string) (map[string]string, error) {
  70. var timestamp = time.Now().UnixMilli()
  71. var nonce = RandomString(12)
  72. str, sign, err := c.GetEncryptMsgDetail(msg, fmt.Sprint(timestamp), nonce)
  73. return map[string]string{"nonce": nonce, "timeStamp": fmt.Sprint(timestamp), "encrypt": str, "msg_signature": sign}, err
  74. }
  75. func (c *DingTalkCrypto) GetEncryptMsgDetail(msg, timestamp, nonce string) (string, string, error) {
  76. size := make([]byte, 4)
  77. binary.BigEndian.PutUint32(size, uint32(len(msg)))
  78. msg = RandomString(16) + string(size) + msg + c.SuiteKey
  79. plantText := pkCS7Padding([]byte(msg), c.Block.BlockSize())
  80. if len(plantText)%aes.BlockSize != 0 {
  81. return "", "", errors.New("ERROR: 消息体size不为16的倍数")
  82. }
  83. blockMode := cipher.NewCBCEncrypter(c.Block, c.BKey[:c.Block.BlockSize()])
  84. chipherText := make([]byte, len(plantText))
  85. blockMode.CryptBlocks(chipherText, plantText)
  86. outMsg := base64.StdEncoding.EncodeToString(chipherText)
  87. signature := c.CreateSignature(c.Token, timestamp, nonce, string(outMsg))
  88. return string(outMsg), signature, nil
  89. }
  90. func sha1Sign(s string) string {
  91. // The pattern for generating a hash is `sha1.New()`,
  92. // `sha1.Write(bytes)`, then `sha1.Sum([]byte{})`.
  93. // Here we start with a new hash.
  94. h := sha1.New()
  95. // `Write` expects bytes. If you have a string `s`,
  96. // use `[]byte(s)` to coerce it to bytes.
  97. h.Write([]byte(s))
  98. // This gets the finalized hash result as a byte
  99. // slice. The argument to `Sum` can be used to append
  100. // to an existing byte slice: it usually isn't needed.
  101. bs := h.Sum(nil)
  102. // SHA1 values are often printed in hex, for example
  103. // in git commits. Use the `%x` format verb to convert
  104. // a hash results to a hex string.
  105. return fmt.Sprintf("%x", bs)
  106. }
  107. // CreateSignature 数据签名
  108. func (c *DingTalkCrypto) CreateSignature(token, timestamp, nonce, msg string) string {
  109. params := make([]string, 0)
  110. params = append(params, token)
  111. params = append(params, timestamp)
  112. params = append(params, nonce)
  113. params = append(params, msg)
  114. sort.Strings(params)
  115. return sha1Sign(strings.Join(params, ""))
  116. }
  117. // VerificationSignature 验证数据签名
  118. func (c *DingTalkCrypto) VerificationSignature(token, timestamp, nonce, msg, sigture string) bool {
  119. return c.CreateSignature(token, timestamp, nonce, msg) == sigture
  120. }
  121. // 解密补位
  122. func pkCS7UnPadding(plantText []byte) []byte {
  123. length := len(plantText)
  124. unpadding := int(plantText[length-1])
  125. return plantText[:(length - unpadding)]
  126. }
  127. // 加密补位
  128. func pkCS7Padding(ciphertext []byte, blockSize int) []byte {
  129. padding := blockSize - len(ciphertext)%blockSize
  130. padtext := bytes.Repeat([]byte{byte(padding)}, padding)
  131. return append(ciphertext, padtext...)
  132. }
  133. // 随机字符串
  134. func RandomString(n int, alphabets ...byte) string {
  135. const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  136. var bytes = make([]byte, n)
  137. var randby bool
  138. if num, err := rand.Read(bytes); num != n || err != nil {
  139. r.Seed(time.Now().UnixNano())
  140. randby = true
  141. }
  142. for i, b := range bytes {
  143. if len(alphabets) == 0 {
  144. if randby {
  145. bytes[i] = alphanum[r.Intn(len(alphanum))]
  146. } else {
  147. bytes[i] = alphanum[b%byte(len(alphanum))]
  148. }
  149. } else {
  150. if randby {
  151. bytes[i] = alphabets[r.Intn(len(alphabets))]
  152. } else {
  153. bytes[i] = alphabets[b%byte(len(alphabets))]
  154. }
  155. }
  156. }
  157. return string(bytes)
  158. }