w7corp/easywechat · error · InvalidArgumentException

Encrypt AES ECB failed.

Error message

Encrypt AES ECB failed.

What it means

Support\AesEcb::encrypt() uses the fixed 'aes-256-ecb' cipher with OPENSSL_RAW_DATA; a false return (surfaced via openssl_error_string(), fallback 'Encrypt AES ECB failed.') throws InvalidArgumentException. AES-256 needs exactly a 32-byte key; in the Pay v2 flow the sibling decrypt uses md5($apiKeyV2) as the key precisely because md5 yields 32 bytes.

Source

Thrown at src/Kernel/Support/AesEcb.php:24

use EasyWeChat\Kernel\Contracts\Aes;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;

use function base64_decode;
use function openssl_decrypt;
use function openssl_error_string;

class AesEcb implements Aes
{
    /**
     * @throws InvalidArgumentException
     */
    public static function encrypt(string $plaintext, string $key, ?string $iv = null): string
    {
        $ciphertext = \openssl_encrypt($plaintext, 'aes-256-ecb', $key, OPENSSL_RAW_DATA, (string) $iv);

        if ($ciphertext === false) {
            throw new InvalidArgumentException(openssl_error_string() ?: 'Encrypt AES ECB failed.');
        }

        return \base64_encode($ciphertext);
    }

    /**
     * @throws InvalidArgumentException
     */
    public static function decrypt(string $ciphertext, string $key, ?string $iv = null): string
    {
        $plaintext = openssl_decrypt(
            base64_decode($ciphertext, true) ?: '',
            'aes-256-ecb',
            $key,
            OPENSSL_RAW_DATA,
            (string) $iv
        );

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Pass a 32-byte key: for WeChat Pay v2 semantics use md5($apiKey), matching Pay\Server's usage
  2. Log openssl_error_string() on failure — it pinpoints the key-length problem
  3. Add a strlen($key) === 32 assertion before calling so mis-sized keys fail loudly at the call site

Example fix

// before
$cipher = AesEcb::encrypt($xml, $apiV2Key); // arbitrary length -> fails

// after
$cipher = AesEcb::encrypt($xml, md5($apiV2Key)); // 32-byte key, matches Pay\Server decrypt
Defensive patterns

Strategy: validation

Validate before calling

// Assert the 32-byte key requirement before encrypting (aes-256-ecb)
if (strlen($key) !== 32) {
    throw new InvalidArgumentException('AesEcb key must be 32 bytes (use md5($apiV2Key) for WeChat Pay v2), got '.strlen($key));
}
$ciphertext = AesEcb::encrypt($plaintext, $key);

Type guard

function isRawAes256Key(string $key): bool
{
    return strlen($key) === 32;
}

Try / catch

try {
    $ciphertext = AesEcb::encrypt($xml, $key);
} catch (\EasyWeChat\Kernel\Exceptions\InvalidArgumentException $e) {
    if (strlen($key) !== 32) {
        $ciphertext = AesEcb::encrypt($xml, md5($key)); // v2 pay semantics
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Calling AesEcb::encrypt() with a key that is not 32 bytes — e.g. the raw WeChat Pay v2 API key (arbitrary length) instead of md5($key), or an undecoded base64 key.

Common situations: Copy-pasting decrypt code into an encrypt path without the md5() transform, key-length assumptions carried over from AES-128 helpers, silent key truncation in config.

Related errors


AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21). Data as JSON: /api/errors/474181bd7aa53fee. Report an issue: GitHub.