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.
wechatopen/aes_crypto.go

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
}