crypto.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. package util
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/hmac"
  7. "crypto/md5"
  8. "crypto/sha256"
  9. "encoding/base64"
  10. "encoding/hex"
  11. "errors"
  12. "fmt"
  13. "hash"
  14. "strings"
  15. )
  16. // 微信签名算法方式
  17. const (
  18. SignTypeMD5 = `MD5`
  19. SignTypeHMACSHA256 = `HMAC-SHA256`
  20. )
  21. // EncryptMsg 加密消息
  22. func EncryptMsg(random, rawXMLMsg []byte, appID, aesKey string) (encrtptMsg []byte, err error) {
  23. defer func() {
  24. if e := recover(); e != nil {
  25. err = fmt.Errorf("panic error: err=%v", e)
  26. return
  27. }
  28. }()
  29. var key []byte
  30. key, err = aesKeyDecode(aesKey)
  31. if err != nil {
  32. panic(err)
  33. }
  34. ciphertext := AESEncryptMsg(random, rawXMLMsg, appID, key)
  35. encrtptMsg = []byte(base64.StdEncoding.EncodeToString(ciphertext))
  36. return
  37. }
  38. // AESEncryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
  39. // 参考:github.com/chanxuehong/wechat.v2
  40. func AESEncryptMsg(random, rawXMLMsg []byte, appID string, aesKey []byte) (ciphertext []byte) {
  41. const (
  42. BlockSize = 32 // PKCS#7
  43. BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时,可以用 mask 获取针对 BLOCK_SIZE 的余数
  44. )
  45. appIDOffset := 20 + len(rawXMLMsg)
  46. contentLen := appIDOffset + len(appID)
  47. amountToPad := BlockSize - contentLen&BlockMask
  48. plaintextLen := contentLen + amountToPad
  49. plaintext := make([]byte, plaintextLen)
  50. // 拼接
  51. copy(plaintext[:16], random)
  52. encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
  53. copy(plaintext[20:], rawXMLMsg)
  54. copy(plaintext[appIDOffset:], appID)
  55. // PKCS#7 补位
  56. for i := contentLen; i < plaintextLen; i++ {
  57. plaintext[i] = byte(amountToPad)
  58. }
  59. // 加密
  60. block, err := aes.NewCipher(aesKey)
  61. if err != nil {
  62. panic(err)
  63. }
  64. mode := cipher.NewCBCEncrypter(block, aesKey[:16])
  65. mode.CryptBlocks(plaintext, plaintext)
  66. ciphertext = plaintext
  67. return
  68. }
  69. // DecryptMsg 消息解密
  70. func DecryptMsg(appID, encryptedMsg, aesKey string) (random, rawMsgXMLBytes []byte, err error) {
  71. defer func() {
  72. if e := recover(); e != nil {
  73. err = fmt.Errorf("panic error: err=%v", e)
  74. return
  75. }
  76. }()
  77. var encryptedMsgBytes, key, getAppIDBytes []byte
  78. encryptedMsgBytes, err = base64.StdEncoding.DecodeString(encryptedMsg)
  79. if err != nil {
  80. return
  81. }
  82. key, err = aesKeyDecode(aesKey)
  83. if err != nil {
  84. panic(err)
  85. }
  86. random, rawMsgXMLBytes, getAppIDBytes, err = AESDecryptMsg(encryptedMsgBytes, key)
  87. if err != nil {
  88. err = fmt.Errorf("消息解密失败,%v", err)
  89. return
  90. }
  91. if appID != string(getAppIDBytes) {
  92. err = fmt.Errorf("消息解密校验 APPID 失败")
  93. return
  94. }
  95. return
  96. }
  97. func aesKeyDecode(encodedAESKey string) (key []byte, err error) {
  98. if len(encodedAESKey) != 43 {
  99. err = fmt.Errorf("the length of encodedAESKey must be equal to 43")
  100. return
  101. }
  102. key, err = base64.StdEncoding.DecodeString(encodedAESKey + "=")
  103. if err != nil {
  104. return
  105. }
  106. if len(key) != 32 {
  107. err = fmt.Errorf("encodingAESKey invalid")
  108. return
  109. }
  110. return
  111. }
  112. // AESDecryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
  113. // 参考:github.com/chanxuehong/wechat.v2
  114. func AESDecryptMsg(ciphertext []byte, aesKey []byte) (random, rawXMLMsg, appID []byte, err error) {
  115. const (
  116. BlockSize = 32 // PKCS#7
  117. BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时,可以用 mask 获取针对 BLOCK_SIZE 的余数
  118. )
  119. if len(ciphertext) < BlockSize {
  120. err = fmt.Errorf("the length of ciphertext too short: %d", len(ciphertext))
  121. return
  122. }
  123. if len(ciphertext)&BlockMask != 0 {
  124. err = fmt.Errorf("ciphertext is not a multiple of the block size, the length is %d", len(ciphertext))
  125. return
  126. }
  127. plaintext := make([]byte, len(ciphertext)) // len(plaintext) >= BLOCK_SIZE
  128. // 解密
  129. block, err := aes.NewCipher(aesKey)
  130. if err != nil {
  131. panic(err)
  132. }
  133. mode := cipher.NewCBCDecrypter(block, aesKey[:16])
  134. mode.CryptBlocks(plaintext, ciphertext)
  135. // PKCS#7 去除补位
  136. amountToPad := int(plaintext[len(plaintext)-1])
  137. if amountToPad < 1 || amountToPad > BlockSize {
  138. err = fmt.Errorf("the amount to pad is incorrect: %d", amountToPad)
  139. return
  140. }
  141. plaintext = plaintext[:len(plaintext)-amountToPad]
  142. // 反拼接
  143. // len(plaintext) == 16+4+len(rawXMLMsg)+len(appId)
  144. if len(plaintext) <= 20 {
  145. err = fmt.Errorf("plaintext too short, the length is %d", len(plaintext))
  146. return
  147. }
  148. rawXMLMsgLen := int(decodeNetworkByteOrder(plaintext[16:20]))
  149. if rawXMLMsgLen < 0 {
  150. err = fmt.Errorf("incorrect msg length: %d", rawXMLMsgLen)
  151. return
  152. }
  153. appIDOffset := 20 + rawXMLMsgLen
  154. if len(plaintext) <= appIDOffset {
  155. err = fmt.Errorf("msg length too large: %d", rawXMLMsgLen)
  156. return
  157. }
  158. random = plaintext[:16:20]
  159. rawXMLMsg = plaintext[20:appIDOffset:appIDOffset]
  160. appID = plaintext[appIDOffset:]
  161. return
  162. }
  163. // 把整数 n 格式化成 4 字节的网络字节序
  164. func encodeNetworkByteOrder(orderBytes []byte, n uint32) {
  165. orderBytes[0] = byte(n >> 24)
  166. orderBytes[1] = byte(n >> 16)
  167. orderBytes[2] = byte(n >> 8)
  168. orderBytes[3] = byte(n)
  169. }
  170. // 从 4 字节的网络字节序里解析出整数
  171. func decodeNetworkByteOrder(orderBytes []byte) (n uint32) {
  172. return uint32(orderBytes[0])<<24 |
  173. uint32(orderBytes[1])<<16 |
  174. uint32(orderBytes[2])<<8 |
  175. uint32(orderBytes[3])
  176. }
  177. // CalculateSign 计算签名
  178. func CalculateSign(content, signType, key string) (string, error) {
  179. var h hash.Hash
  180. if signType == SignTypeHMACSHA256 {
  181. h = hmac.New(sha256.New, []byte(key))
  182. } else {
  183. h = md5.New()
  184. }
  185. if _, err := h.Write([]byte(content)); err != nil {
  186. return ``, err
  187. }
  188. return strings.ToUpper(hex.EncodeToString(h.Sum(nil))), nil
  189. }
  190. // ParamSign 计算所传参数的签名
  191. func ParamSign(p map[string]string, key string) (string, error) {
  192. bizKey := "&key=" + key
  193. str := OrderParam(p, bizKey)
  194. var signType string
  195. switch p["sign_type"] {
  196. case SignTypeMD5, SignTypeHMACSHA256:
  197. signType = p["sign_type"]
  198. case ``:
  199. signType = SignTypeMD5
  200. default:
  201. return ``, errors.New(`invalid sign_type`)
  202. }
  203. return CalculateSign(str, signType, key)
  204. }
  205. // ECB provides confidentiality by assigning a fixed ciphertext block to each plaintext block.
  206. // See NIST SP 800-38A, pp 08-09
  207. // reference: https://codereview.appspot.com/7860047/patch/23001/24001
  208. type ecb struct {
  209. b cipher.Block
  210. blockSize int
  211. }
  212. func newECB(b cipher.Block) *ecb {
  213. return &ecb{
  214. b: b,
  215. blockSize: b.BlockSize(),
  216. }
  217. }
  218. // ECBEncryptor -
  219. type ECBEncryptor ecb
  220. // NewECBEncryptor returns a BlockMode which encrypts in electronic code book mode, using the given Block.
  221. func NewECBEncryptor(b cipher.Block) cipher.BlockMode {
  222. return (*ECBEncryptor)(newECB(b))
  223. }
  224. // BlockSize implement BlockMode.BlockSize
  225. func (x *ECBEncryptor) BlockSize() int {
  226. return x.blockSize
  227. }
  228. // CryptBlocks implement BlockMode.CryptBlocks
  229. func (x *ECBEncryptor) CryptBlocks(dst, src []byte) {
  230. if len(src)%x.blockSize != 0 {
  231. panic("crypto/cipher: input not full blocks")
  232. }
  233. if len(dst) < len(src) {
  234. panic("crypto/cipher: output smaller than input")
  235. }
  236. for len(src) > 0 {
  237. x.b.Encrypt(dst, src[:x.blockSize])
  238. src = src[x.blockSize:]
  239. dst = dst[x.blockSize:]
  240. }
  241. }
  242. // ECBDecryptor -
  243. type ECBDecryptor ecb
  244. // NewECBDecryptor returns a BlockMode which decrypts in electronic code book mode, using the given Block.
  245. func NewECBDecryptor(b cipher.Block) cipher.BlockMode {
  246. return (*ECBDecryptor)(newECB(b))
  247. }
  248. // BlockSize implement BlockMode.BlockSize
  249. func (x *ECBDecryptor) BlockSize() int {
  250. return x.blockSize
  251. }
  252. // CryptBlocks implement BlockMode.CryptBlocks
  253. func (x *ECBDecryptor) CryptBlocks(dst, src []byte) {
  254. if len(src)%x.blockSize != 0 {
  255. panic("crypto/cipher: input not full blocks")
  256. }
  257. if len(dst) < len(src) {
  258. panic("crypto/cipher: output smaller than input")
  259. }
  260. for len(src) > 0 {
  261. x.b.Decrypt(dst, src[:x.blockSize])
  262. src = src[x.blockSize:]
  263. dst = dst[x.blockSize:]
  264. }
  265. }
  266. // AesECBDecrypt will decrypt data with PKCS5Padding
  267. func AesECBDecrypt(ciphertext []byte, aesKey []byte) ([]byte, error) {
  268. if len(ciphertext) < aes.BlockSize {
  269. return nil, errors.New("ciphertext too short")
  270. }
  271. // ECB mode always works in whole blocks.
  272. if len(ciphertext)%aes.BlockSize != 0 {
  273. return nil, errors.New("ciphertext is not a multiple of the block size")
  274. }
  275. block, err := aes.NewCipher(aesKey)
  276. if err != nil {
  277. return nil, err
  278. }
  279. NewECBDecryptor(block).CryptBlocks(ciphertext, ciphertext)
  280. return PKCS5UnPadding(ciphertext), nil
  281. }
  282. // PKCS5Padding -
  283. func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
  284. padding := blockSize - len(ciphertext)%blockSize
  285. padText := bytes.Repeat([]byte{byte(padding)}, padding)
  286. return append(ciphertext, padText...)
  287. }
  288. // PKCS5UnPadding -
  289. func PKCS5UnPadding(origData []byte) []byte {
  290. length := len(origData)
  291. unPadding := int(origData[length-1])
  292. return origData[:(length - unPadding)]
  293. }