crypto.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package util
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/md5"
  7. "crypto/rand"
  8. "crypto/sha1"
  9. "encoding/base64"
  10. "encoding/hex"
  11. "errors"
  12. "io"
  13. "sort"
  14. "strings"
  15. "github.com/sqeven/weapp/util/ecb"
  16. )
  17. // SignByMD5 多参数通过MD5签名
  18. func SignByMD5(data map[string]string, key string) (string, error) {
  19. var query []string
  20. for k, v := range data {
  21. query = append(query, k+"="+v)
  22. }
  23. sort.Strings(query)
  24. query = append(query, "key="+key)
  25. str := strings.Join(query, "&")
  26. str, err := MD5(str)
  27. if err != nil {
  28. return "", err
  29. }
  30. return strings.ToUpper(str), nil
  31. }
  32. // MD5 加密
  33. func MD5(str string) (string, error) {
  34. hs := md5.New()
  35. if _, err := hs.Write([]byte(str)); err != nil {
  36. return "", err
  37. }
  38. return hex.EncodeToString(hs.Sum(nil)), nil
  39. }
  40. // PKCS5UnPadding 反补
  41. // Golang AES没有64位的块, 如果采用PKCS5, 那么实质上就是采用PKCS7
  42. func PKCS5UnPadding(plaintext []byte) ([]byte, error) {
  43. ln := len(plaintext)
  44. // 去掉最后一个字节 unPadding 次
  45. unPadding := int(plaintext[ln-1])
  46. if unPadding > ln {
  47. return []byte{}, errors.New("数据不正确")
  48. }
  49. return plaintext[:(ln - unPadding)], nil
  50. }
  51. // PKCS5Padding 补位
  52. // Golang AES没有64位的块, 如果采用PKCS5, 那么实质上就是采用PKCS7
  53. func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
  54. padding := blockSize - len(ciphertext)%blockSize
  55. padtext := bytes.Repeat([]byte{byte(padding)}, padding)
  56. return append(ciphertext, padtext...)
  57. }
  58. // Validate 对数据包进行签名校验,确保数据的完整性。
  59. //
  60. // @rawData 不包括敏感信息的原始数据字符串,用于计算签名。
  61. // @signature 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息
  62. // @ssk 微信 session_key
  63. func Validate(rawData, ssk, signature string) bool {
  64. r := sha1.Sum([]byte(rawData + ssk))
  65. return signature == hex.EncodeToString(r[:])
  66. }
  67. // CBCDecrypt CBC解密数据
  68. //
  69. // @ssk 通过 Login 向微信服务端请求得到的 session_key
  70. // @data 小程序通过 api 得到的加密数据(encryptedData)
  71. // @iv 小程序通过 api 得到的初始向量(iv)
  72. func CBCDecrypt(ssk, data, iv string) (bts []byte, err error) {
  73. key, err := base64.StdEncoding.DecodeString(ssk)
  74. if err != nil {
  75. return
  76. }
  77. ciphertext, err := base64.StdEncoding.DecodeString(data)
  78. if err != nil {
  79. return
  80. }
  81. rawIV, err := base64.StdEncoding.DecodeString(iv)
  82. if err != nil {
  83. return
  84. }
  85. block, err := aes.NewCipher(key)
  86. if err != nil {
  87. return
  88. }
  89. size := aes.BlockSize
  90. // The IV needs to be unique, but not secure. Therefore it's common to
  91. // include it at the beginning of the ciphertext.
  92. if len(ciphertext) < size {
  93. err = errors.New("cipher too short")
  94. return
  95. }
  96. // CBC mode always works in whole blocks.
  97. if len(ciphertext)%size != 0 {
  98. err = errors.New("cipher is not a multiple of the block size")
  99. return
  100. }
  101. mode := cipher.NewCBCDecrypter(block, rawIV[:size])
  102. plaintext := make([]byte, len(ciphertext))
  103. mode.CryptBlocks(plaintext, ciphertext)
  104. return PKCS5UnPadding(plaintext)
  105. }
  106. // CBCEncrypt CBC加密数据
  107. func CBCEncrypt(key, data string) (ciphertext []byte, err error) {
  108. dk, err := hex.DecodeString(key)
  109. if err != nil {
  110. return
  111. }
  112. plaintext := []byte(data)
  113. if len(plaintext)%aes.BlockSize != 0 {
  114. err = errors.New("plaintext is not a multiple of the block size")
  115. return
  116. }
  117. block, err := aes.NewCipher(dk)
  118. if err != nil {
  119. return
  120. }
  121. ciphertext = make([]byte, aes.BlockSize+len(plaintext))
  122. iv := ciphertext[:aes.BlockSize]
  123. if _, err = io.ReadFull(rand.Reader, iv); err != nil {
  124. return
  125. }
  126. cipher.NewCBCEncrypter(block, iv).CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
  127. return PKCS5Padding(ciphertext, block.BlockSize()), nil
  128. }
  129. // AesECBDecrypt CBC解密数据
  130. //
  131. // @ciphertext 加密数据
  132. // @key 商户支付密钥
  133. func AesECBDecrypt(ciphertext, key []byte) (plaintext []byte, err error) {
  134. if len(ciphertext) < aes.BlockSize {
  135. return nil, errors.New("cipher too short")
  136. }
  137. // ECB mode always works in whole blocks.
  138. if len(ciphertext)%aes.BlockSize != 0 {
  139. return nil, errors.New("cipher is not a multiple of the block size")
  140. }
  141. block, err := aes.NewCipher(key)
  142. if err != nil {
  143. return
  144. }
  145. ecb.NewDecrypter(block).CryptBlocks(ciphertext, ciphertext)
  146. return PKCS5UnPadding(ciphertext)
  147. }