chenhg5/cc-connect · error

invalid pkcs7 padding

Error message

invalid pkcs7 padding

What it means

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.

Source

Thrown at platform/weixin/cdn.go:46

	return ((plaintextLen + aes.BlockSize) / aes.BlockSize) * aes.BlockSize
}

func pkcs7Pad(b []byte, blockSize int) []byte {
	if blockSize <= 0 || blockSize > 255 {
		panic("invalid block size")
	}
	n := blockSize - (len(b) % blockSize)
	pad := bytes.Repeat([]byte{byte(n)}, n)
	return append(b, pad...)
}

func pkcs7Unpad(b []byte, blockSize int) ([]byte, error) {
	if len(b) == 0 || len(b)%blockSize != 0 {
		return nil, fmt.Errorf("invalid padded length %d", len(b))
	}
	n := int(b[len(b)-1])
	if n == 0 || n > blockSize || n > len(b) {
		return nil, fmt.Errorf("invalid pkcs7 padding")
	}
	for i := len(b) - n; i < len(b); i++ {
		if b[i] != byte(n) {
			return nil, fmt.Errorf("invalid pkcs7 padding")
		}
	}
	return b[:len(b)-n], nil
}

func encryptAESECB(plaintext, key []byte) ([]byte, error) {
	if len(key) != 16 {
		return nil, fmt.Errorf("aes key must be 16 bytes, got %d", len(key))
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	padded := pkcs7Pad(plaintext, aes.BlockSize)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Verify the aes_key belongs to this specific CDNMedia item (do not reuse keys across files)
  2. Re-download the media — the ciphertext may be corrupted or an error body
  3. Confirm the payload is actually AES-ECB-encrypted CDN data before decrypting
  4. Log the key hash and media URL to correlate key and file

Example fix

key, err := parseAesKey(media.AesKey, media.URL)
if err != nil { return err } // key tied to THIS media, not a cached/global key
plain, err := decryptAESECB(cipher, key)
if err != nil {
    return fmt.Errorf("cdn: decrypt %s (key mismatch or corrupt file?): %w", media.URL, err)
}
Defensive patterns

Strategy: validation

Validate before calling

key, err := parseAesKey(media.AesKey, media.URL)
if err != nil { return err }
// key now guaranteed to be the 16 bytes bound to this media item

Try / catch

plain, err := decryptAESECB(cipher, key)
if err != nil {
    if strings.Contains(err.Error(), "invalid pkcs7 padding") {
        // wrong key or corrupt data — refetch key + media, don't reuse
        return refreshKeyAndRedownload(media)
    }
    return err
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/e679edd05abe35c7. Report an issue: GitHub.