w7corp/easywechat · error · InvalidArgumentException
Encrypt failed
Error message
Encrypt failed
What it means
Thrown when openssl_encrypt() returns false for aes-256-gcm. AesGcm::encrypt is the SDK's GCM helper (no internal caller; WeChat Pay v3-style encryption): it uses OPENSSL_RAW_DATA, a 16-byte tag, and returns base64(ciphertext.tag). OpenSSL fails when the key is not exactly 32 bytes, when the IV is empty — (string) null === '' and GCM requires a non-empty nonce — or on unsupported tag lengths, so the dominant cause in this code is a null/empty $iv or a key of the wrong length.
Source
Thrown at src/Kernel/Support/AesGcm.php:37
/**
* @throws InvalidArgumentException
*/
public static function encrypt(string $plaintext, string $key, ?string $iv = null, string $aad = ''): string
{
$ciphertext = openssl_encrypt(
$plaintext,
'aes-256-gcm',
$key,
OPENSSL_RAW_DATA,
(string) $iv,
$tag,
$aad,
self::BLOCK_SIZE
);
if ($ciphertext === false) {
throw new InvalidArgumentException(openssl_error_string() ?: 'Encrypt failed');
}
return base64_encode($ciphertext.$tag);
}
/**
* @throws InvalidArgumentException
*/
public static function decrypt(string $ciphertext, string $key, ?string $iv = null, string $aad = ''): string
{
$ciphertext = base64_decode($ciphertext);
$tag = substr($ciphertext, -self::BLOCK_SIZE);
$ciphertext = substr($ciphertext, 0, -self::BLOCK_SIZE);
$plaintext = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, (string) $iv, $tag, $aad);
View on GitHub (pinned to f0cf0a8b83)
Solutions
- Pass a non-empty nonce, normally 12 random bytes: AesGcm::encrypt($plain, $key, random_bytes(12), $aad).
- Check strlen($key) === 32 — aes-256-gcm accepts exactly 32 bytes.
- trim() keys and nonces loaded from config/env files.
- Confirm GCM support: php -r 'var_dump(in_array("aes-256-gcm", openssl_get_cipher_methods()));'.
Example fix
// before: null iv -> (string) null === '' -> openssl_encrypt returns false $ct = AesGcm::encrypt($plain, $key, iv: null, aad: 'transaction'); // after: explicit 12-byte nonce with a 32-byte key $ct = AesGcm::encrypt($plain, $key, random_bytes(12), 'transaction');
Defensive patterns
Strategy: validation
Validate before calling
if (strlen($key) !== 32) { throw new InvalidArgumentException('key must be 32 bytes for aes-256-gcm'); }
$nonce = $nonce ?? random_bytes(12);
if ($nonce === '') { throw new InvalidArgumentException('GCM nonce must not be empty'); }
$cipher = AesGcm::encrypt($plain, $key, $nonce, $aad); Try / catch
try { $cipher = AesGcm::encrypt($plain, $key, $nonce, $aad); } catch (\EasyWeChat\Kernel\Exceptions\InvalidArgumentException $e) { // openssl_error_string() output is in the message throw new RuntimeException('Failed to encrypt payload: '.$e->getMessage(), 0, $e); } Prevention
- Centralize nonce generation with random_bytes(12)
- Assert strlen($key) === 32 once at bootstrap
- Never reuse a nonce with the same key
When it happens
Trigger: Calling AesGcm::encrypt($plain, $key, iv: null) or iv: '' with aes-256-gcm (empty nonce makes openssl_encrypt return false); passing a key whose strlen() is not 32 (e.g. a 43-char EncodingAESKey used verbatim, or an APIv3 key with a trailing newline); calling with a key byte length of 16/24 copied from aes-128 code.
Common situations: The 32-char APIv3 key loaded from an env var that got quoted or whitespace-padded; reusing the WeChat EncodingAESKey without base64-decoding it; hand-rolled nonce generation that can return an empty string.
Related errors
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/87db5ec80a0f5ac5.
Report an issue: GitHub.