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 }