chenhg5/cc-connect · error

wecom-ws: invalid pkcs7 pad length %d

Error message

wecom-ws: invalid pkcs7 pad length %d

What it means

pkcs7UnpadWeCom reads the last byte as the PKCS7 pad length and validates it is between 1 and 32 (WeCom uses up to 32-byte padding) and not longer than the data. If the final byte is 0 or >32 (or exceeds the buffer), the padding is structurally impossible and the data is corrupt — usually a wrong AES key produced garbage plaintext.

Source

Thrown at platform/wecom/websocket_media.go:299

	block, err := aes.NewCipher(key32)
	if err != nil {
		return nil, err
	}
	if len(ciphertext)%aes.BlockSize != 0 {
		return nil, fmt.Errorf("wecom-ws: ciphertext not multiple of block size")
	}
	plain := make([]byte, len(ciphertext))
	cipher.NewCBCDecrypter(block, iv).CryptBlocks(plain, ciphertext)
	return pkcs7UnpadWeCom(plain)
}

func pkcs7UnpadWeCom(data []byte) ([]byte, error) {
	if len(data) == 0 {
		return nil, fmt.Errorf("wecom-ws: empty padded data")
	}
	padLen := int(data[len(data)-1])
	if padLen < 1 || padLen > 32 || padLen > len(data) {
		return nil, fmt.Errorf("wecom-ws: invalid pkcs7 pad length %d", padLen)
	}
	for i := len(data) - padLen; i < len(data); i++ {
		if int(data[i]) != padLen {
			return nil, fmt.Errorf("wecom-ws: invalid pkcs7 padding")
		}
	}
	return data[:len(data)-padLen], nil
}

func parseContentDispositionFilename(h string) string {
	h = strings.TrimSpace(h)
	if h == "" {
		return ""
	}
	lower := strings.ToLower(h)
	// RFC 5987: filename*=UTF-8''percent-encoded
	if idx := strings.Index(lower, "filename*="); idx >= 0 {
		val := strings.TrimSpace(h[idx+len("filename*="):])

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Verify the configured EncodingAESKey matches the bot that produced the media URL; re-sync after any key rotation.
  2. Treat this as a wrong-key symptom: if it occurs consistently, the key bytes are wrong even though base64 decoding succeeded.
  3. Confirm the media is WeCom AES-256-CBC with IV = first 16 key bytes (not a standard random-IV format).

Example fix

// before
key := loadOldKey() // rotated in console
plain, err := wecomDecryptFile(ct, key) // fails: invalid pkcs7 pad length
// after
key := loadKeyFromConsole() // re-fetched current EncodingAESKey
plain, err := wecomDecryptFile(ct, key)
Defensive patterns

Strategy: try-catch

Validate before calling

k, err := base64.StdEncoding.DecodeString(key)
if err != nil || len(k) != 32 { /* fix config before runtime */ }

Try / catch

plain, err := wecomDecryptFile(ct, key)
if err != nil && strings.Contains(err.Error(), "pkcs7") {
    return fmt.Errorf("decryption failed — likely wrong EncodingAESKey for this bot: %w", err)
}

Prevention

When it happens

Trigger: wecomDecryptFile decrypts with a wrong/mismatched AES key; the last plaintext byte is then a random value (often 0x00 or >32) failing the pad-length sanity check.

Common situations: EncodingAESKey rotated in the WeCom console but stale key in config; decrypting media from a different bot than the one whose key was configured; key decoded with wrong padding earlier.

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/87bf7102c70b2472. Report an issue: GitHub.