crypto.go 4.8 KB

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