{"record":{"id":"e679edd05abe35c7","repo":"chenhg5/cc-connect","slug":"invalid-pkcs7-padding","errorCode":null,"errorMessage":"invalid pkcs7 padding","messagePattern":"invalid pkcs7 padding","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/weixin/cdn.go","lineNumber":46,"sourceCode":"\treturn ((plaintextLen + aes.BlockSize) / aes.BlockSize) * aes.BlockSize\n}\n\nfunc pkcs7Pad(b []byte, blockSize int) []byte {\n\tif blockSize <= 0 || blockSize > 255 {\n\t\tpanic(\"invalid block size\")\n\t}\n\tn := blockSize - (len(b) % blockSize)\n\tpad := bytes.Repeat([]byte{byte(n)}, n)\n\treturn append(b, pad...)\n}\n\nfunc pkcs7Unpad(b []byte, blockSize int) ([]byte, error) {\n\tif len(b) == 0 || len(b)%blockSize != 0 {\n\t\treturn nil, fmt.Errorf(\"invalid padded length %d\", len(b))\n\t}\n\tn := int(b[len(b)-1])\n\tif n == 0 || n > blockSize || n > len(b) {\n\t\treturn nil, fmt.Errorf(\"invalid pkcs7 padding\")\n\t}\n\tfor i := len(b) - n; i < len(b); i++ {\n\t\tif b[i] != byte(n) {\n\t\t\treturn nil, fmt.Errorf(\"invalid pkcs7 padding\")\n\t\t}\n\t}\n\treturn b[:len(b)-n], nil\n}\n\nfunc encryptAESECB(plaintext, key []byte) ([]byte, error) {\n\tif len(key) != 16 {\n\t\treturn nil, fmt.Errorf(\"aes key must be 16 bytes, got %d\", len(key))\n\t}\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpadded := pkcs7Pad(plaintext, aes.BlockSize)","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/weixin/cdn.go#L28-L64","documentation":"After checking range and length, pkcs7Unpad verifies that the final n bytes all equal the pad byte value n; if any byte differs the buffer does not have valid PKCS7 padding and this error is returned. Because AES-ECB is deterministic, a padding mismatch after successful decryption almost always means the wrong key or wrong ciphertext — the data decrypted to something that is not validly padded plaintext.","triggerScenarios":"Downloading WeChat CDN media with an aes_key that does not match the file; passing a key for a different media item; corrupted or truncated ciphertext that still happens to be block-aligned; decrypting data that was never PKCS7-padded (raw binary).","commonSituations":"Mixing up aes_key fields between concurrent CDN downloads; CDN returning an error page or HTML of exactly block-aligned length instead of the file; client/server key mismatch after re-upload.","solutions":["Verify the aes_key belongs to this specific CDNMedia item (do not reuse keys across files)","Re-download the media — the ciphertext may be corrupted or an error body","Confirm the payload is actually AES-ECB-encrypted CDN data before decrypting","Log the key hash and media URL to correlate key and file"],"exampleFix":"key, err := parseAesKey(media.AesKey, media.URL)\nif err != nil { return err } // key tied to THIS media, not a cached/global key\nplain, err := decryptAESECB(cipher, key)\nif err != nil {\n    return fmt.Errorf(\"cdn: decrypt %s (key mismatch or corrupt file?): %w\", media.URL, err)\n}","handlingStrategy":"validation","validationCode":"key, err := parseAesKey(media.AesKey, media.URL)\nif err != nil { return err }\n// key now guaranteed to be the 16 bytes bound to this media item","typeGuard":null,"tryCatchPattern":"plain, err := decryptAESECB(cipher, key)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid pkcs7 padding\") {\n        // wrong key or corrupt data — refetch key + media, don't reuse\n        return refreshKeyAndRedownload(media)\n    }\n    return err\n}","preventionTips":["Bind each aes_key to its specific media item; never share keys across downloads","Refetch media metadata if decryption fails — keys may have been rotated","Do not attempt to use plaintext after a padding error (it is garbage)","Verify expected file size/hash after decryption when available"],"tags":["weixin","crypto","pkcs7","aes-ecb"],"backgroundTag":"checksum-mismatch","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}