crypto.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package util
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/aes"
  6. "crypto/cipher"
  7. "crypto/md5"
  8. "encoding/base64"
  9. "encoding/hex"
  10. "fmt"
  11. )
  12. //EncryptMsg 加密消息
  13. func EncryptMsg(random, rawXMLMsg []byte, appID, aesKey string) (encrtptMsg []byte, err error) {
  14. defer func() {
  15. if e := recover(); e != nil {
  16. err = fmt.Errorf("panic error: err=%v", e)
  17. return
  18. }
  19. }()
  20. var key []byte
  21. key, err = aesKeyDecode(aesKey)
  22. if err != nil {
  23. panic(err)
  24. }
  25. ciphertext := AESEncryptMsg(random, rawXMLMsg, appID, key)
  26. encrtptMsg = []byte(base64.StdEncoding.EncodeToString(ciphertext))
  27. return
  28. }
  29. //AESEncryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
  30. //参考:github.com/chanxuehong/wechat.v2
  31. func AESEncryptMsg(random, rawXMLMsg []byte, appID string, aesKey []byte) (ciphertext []byte) {
  32. const (
  33. BlockSize = 32 // PKCS#7
  34. BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
  35. )
  36. appIDOffset := 20 + len(rawXMLMsg)
  37. contentLen := appIDOffset + len(appID)
  38. amountToPad := BlockSize - contentLen&BlockMask
  39. plaintextLen := contentLen + amountToPad
  40. plaintext := make([]byte, plaintextLen)
  41. // 拼接
  42. copy(plaintext[:16], random)
  43. encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
  44. copy(plaintext[20:], rawXMLMsg)
  45. copy(plaintext[appIDOffset:], appID)
  46. // PKCS#7 补位
  47. for i := contentLen; i < plaintextLen; i++ {
  48. plaintext[i] = byte(amountToPad)
  49. }
  50. // 加密
  51. block, err := aes.NewCipher(aesKey[:])
  52. if err != nil {
  53. panic(err)
  54. }
  55. mode := cipher.NewCBCEncrypter(block, aesKey[:16])
  56. mode.CryptBlocks(plaintext, plaintext)
  57. ciphertext = plaintext
  58. return
  59. }
  60. //DecryptMsg 消息解密
  61. func DecryptMsg(appID, encryptedMsg, aesKey string) (random, rawMsgXMLBytes []byte, err error) {
  62. defer func() {
  63. if e := recover(); e != nil {
  64. err = fmt.Errorf("panic error: err=%v", e)
  65. return
  66. }
  67. }()
  68. var encryptedMsgBytes, key, getAppIDBytes []byte
  69. encryptedMsgBytes, err = base64.StdEncoding.DecodeString(encryptedMsg)
  70. if err != nil {
  71. return
  72. }
  73. key, err = aesKeyDecode(aesKey)
  74. if err != nil {
  75. panic(err)
  76. }
  77. random, rawMsgXMLBytes, getAppIDBytes, err = AESDecryptMsg(encryptedMsgBytes, key)
  78. if err != nil {
  79. err = fmt.Errorf("消息解密失败,%v", err)
  80. return
  81. }
  82. if appID != string(getAppIDBytes) {
  83. err = fmt.Errorf("消息解密校验APPID失败")
  84. return
  85. }
  86. return
  87. }
  88. func aesKeyDecode(encodedAESKey string) (key []byte, err error) {
  89. if len(encodedAESKey) != 43 {
  90. err = fmt.Errorf("the length of encodedAESKey must be equal to 43")
  91. return
  92. }
  93. key, err = base64.StdEncoding.DecodeString(encodedAESKey + "=")
  94. if err != nil {
  95. return
  96. }
  97. if len(key) != 32 {
  98. err = fmt.Errorf("encodingAESKey invalid")
  99. return
  100. }
  101. return
  102. }
  103. // AESDecryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
  104. //参考:github.com/chanxuehong/wechat.v2
  105. func AESDecryptMsg(ciphertext []byte, aesKey []byte) (random, rawXMLMsg, appID []byte, err error) {
  106. const (
  107. BlockSize = 32 // PKCS#7
  108. BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
  109. )
  110. if len(ciphertext) < BlockSize {
  111. err = fmt.Errorf("the length of ciphertext too short: %d", len(ciphertext))
  112. return
  113. }
  114. if len(ciphertext)&BlockMask != 0 {
  115. err = fmt.Errorf("ciphertext is not a multiple of the block size, the length is %d", len(ciphertext))
  116. return
  117. }
  118. plaintext := make([]byte, len(ciphertext)) // len(plaintext) >= BLOCK_SIZE
  119. // 解密
  120. block, err := aes.NewCipher(aesKey)
  121. if err != nil {
  122. panic(err)
  123. }
  124. mode := cipher.NewCBCDecrypter(block, aesKey[:16])
  125. mode.CryptBlocks(plaintext, ciphertext)
  126. // PKCS#7 去除补位
  127. amountToPad := int(plaintext[len(plaintext)-1])
  128. if amountToPad < 1 || amountToPad > BlockSize {
  129. err = fmt.Errorf("the amount to pad is incorrect: %d", amountToPad)
  130. return
  131. }
  132. plaintext = plaintext[:len(plaintext)-amountToPad]
  133. // 反拼接
  134. // len(plaintext) == 16+4+len(rawXMLMsg)+len(appId)
  135. if len(plaintext) <= 20 {
  136. err = fmt.Errorf("plaintext too short, the length is %d", len(plaintext))
  137. return
  138. }
  139. rawXMLMsgLen := int(decodeNetworkByteOrder(plaintext[16:20]))
  140. if rawXMLMsgLen < 0 {
  141. err = fmt.Errorf("incorrect msg length: %d", rawXMLMsgLen)
  142. return
  143. }
  144. appIDOffset := 20 + rawXMLMsgLen
  145. if len(plaintext) <= appIDOffset {
  146. err = fmt.Errorf("msg length too large: %d", rawXMLMsgLen)
  147. return
  148. }
  149. random = plaintext[:16:20]
  150. rawXMLMsg = plaintext[20:appIDOffset:appIDOffset]
  151. appID = plaintext[appIDOffset:]
  152. return
  153. }
  154. // 把整数 n 格式化成 4 字节的网络字节序
  155. func encodeNetworkByteOrder(orderBytes []byte, n uint32) {
  156. orderBytes[0] = byte(n >> 24)
  157. orderBytes[1] = byte(n >> 16)
  158. orderBytes[2] = byte(n >> 8)
  159. orderBytes[3] = byte(n)
  160. }
  161. // 从 4 字节的网络字节序里解析出整数
  162. func decodeNetworkByteOrder(orderBytes []byte) (n uint32) {
  163. return uint32(orderBytes[0])<<24 |
  164. uint32(orderBytes[1])<<16 |
  165. uint32(orderBytes[2])<<8 |
  166. uint32(orderBytes[3])
  167. }
  168. // MD5Sum 计算 32 位长度的 MD5 sum
  169. func MD5Sum(txt string) (sum string) {
  170. h := md5.New()
  171. buf := bufio.NewWriterSize(h, 128)
  172. buf.WriteString(txt)
  173. buf.Flush()
  174. sign := make([]byte, hex.EncodedLen(h.Size()))
  175. hex.Encode(sign, h.Sum(nil))
  176. sum = string(bytes.ToUpper(sign))
  177. return
  178. }