You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
3.0 KiB
126 lines
3.0 KiB
package wechatopen
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"encoding/base64"
|
|
"fmt"
|
|
)
|
|
|
|
const (
|
|
BLOCK_SIZE = 32 // PKCS#7
|
|
BLOCK_MASK = BLOCK_SIZE - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
|
|
)
|
|
|
|
// 把整数 n 格式化成 4 字节的网络字节序
|
|
func encodeNetworkByteOrder(b []byte, n uint32) {
|
|
b[0] = byte(n >> 24)
|
|
b[1] = byte(n >> 16)
|
|
b[2] = byte(n >> 8)
|
|
b[3] = byte(n)
|
|
}
|
|
|
|
// 从 4 字节的网络字节序里解析出整数
|
|
func decodeNetworkByteOrder(b []byte) (n uint32) {
|
|
return uint32(b[0])<<24 |
|
|
uint32(b[1])<<16 |
|
|
uint32(b[2])<<8 |
|
|
uint32(b[3])
|
|
}
|
|
|
|
// AESEncryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
|
|
func AESEncryptMsg(random, rawXMLMsg []byte, appId string, aesKey []byte) (ciphertext []byte) {
|
|
appIdOffset := 20 + len(rawXMLMsg)
|
|
contentLen := appIdOffset + len(appId)
|
|
amountToPad := BLOCK_SIZE - contentLen&BLOCK_MASK
|
|
plaintextLen := contentLen + amountToPad
|
|
|
|
plaintext := make([]byte, plaintextLen)
|
|
|
|
// 拼接
|
|
copy(plaintext[:16], random)
|
|
encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
|
|
copy(plaintext[20:], rawXMLMsg)
|
|
copy(plaintext[appIdOffset:], appId)
|
|
|
|
// PKCS#7 补位
|
|
for i := contentLen; i < plaintextLen; i++ {
|
|
plaintext[i] = byte(amountToPad)
|
|
}
|
|
|
|
// 加密
|
|
block, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
mode := cipher.NewCBCEncrypter(block, aesKey[:16])
|
|
mode.CryptBlocks(plaintext, plaintext)
|
|
|
|
ciphertext = plaintext
|
|
return
|
|
}
|
|
|
|
// AESDecryptMsg c解密
|
|
func AESDecryptMsg(decryptStr, aesKey string) (string, error) {
|
|
cipherText, err := base64.StdEncoding.DecodeString(decryptStr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// 解密
|
|
block, err := aes.NewCipher([]byte(aesKey))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
blockMode := cipher.NewCBCDecrypter(block, []byte(aesKey))
|
|
decrypted := make([]byte, len(cipherText))
|
|
blockMode.CryptBlocks(decrypted, cipherText)
|
|
|
|
decrypted = pkcs5UnPadding(decrypted)
|
|
return string(decrypted), nil
|
|
}
|
|
|
|
func pkcs5UnPadding(decrypted []byte) []byte {
|
|
length := len(decrypted)
|
|
unPadding := int(decrypted[length-1])
|
|
return decrypted[:(length - unPadding)]
|
|
}
|
|
|
|
func AESDecryptData(cipherText, aesKey, iv []byte) (rawData []byte, err error) {
|
|
if len(cipherText) < BLOCK_SIZE {
|
|
err = fmt.Errorf("the length of ciphertext too short: %d", len(cipherText))
|
|
return
|
|
}
|
|
|
|
plaintext := make([]byte, len(cipherText)) // len(plaintext) >= BLOCK_SIZE
|
|
|
|
// 解密
|
|
block, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
mode := cipher.NewCBCDecrypter(block, iv)
|
|
mode.CryptBlocks(plaintext, cipherText)
|
|
|
|
// PKCS#7 去除补位
|
|
amountToPad := int(plaintext[len(plaintext)-1])
|
|
if amountToPad < 1 || amountToPad > BLOCK_SIZE {
|
|
err = fmt.Errorf("the amount to pad is incorrect: %d", amountToPad)
|
|
return
|
|
}
|
|
plaintext = plaintext[:len(plaintext)-amountToPad]
|
|
|
|
// 反拼接
|
|
// len(plaintext) == 16+4+len(rawXMLMsg)+len(appId)
|
|
if len(plaintext) <= 20 {
|
|
err = fmt.Errorf("plaintext too short, the length is %d", len(plaintext))
|
|
return
|
|
}
|
|
|
|
rawData = plaintext
|
|
|
|
return
|
|
|
|
}
|