从mcrypt_encrypt迁移至openssl_encrypt(AES256)加密结果不一致问题
Hey there! Let's figure out why your mcrypt_encrypt() and openssl_encrypt() outputs aren't matching—this is a super common pitfall when migrating from mcrypt to openssl, so you're not alone. Here are the key differences that are almost certainly causing the discrepancy:
1. Default Padding Behavior Differs
mcrypt uses zero padding by default (filling leftover space in the block with null bytes), while openssl defaults to PKCS#7 padding (filling with bytes equal to the number of padding bytes needed). This alone will make your outputs totally different if you don't align the padding settings.
To fix this for openssl:
- Use the
OPENSSL_ZERO_PADDINGflag to disable automatic PKCS#7 padding - Manually pad your plaintext with null bytes to reach the AES block size (16 bytes—important: AES always uses 16-byte blocks, even for 256-bit keys)
2. IV Handling Is Strictly Enforced in openssl
mcrypt will silently use a full-null IV if you don't provide one, but openssl requires a valid IV for CBC mode (which you're almost certainly using, since ECB is insecure). If you don't pass an IV to openssl, it may throw an error or use an unpredictable value—either way, the encryption result won't match.
Fix: Make sure you use the exact same IV in both functions. If your original mcrypt code didn't specify an IV, generate a full-null 16-byte IV for openssl.
3. Key Length Handling Isn't Consistent
mcrypt will automatically truncate or pad your key with null bytes to reach the required length (32 bytes for AES-256), but openssl will throw an error if your key is the wrong length. You need to replicate mcrypt's key behavior manually in openssl.
Fix: Adjust your key to 32 bytes explicitly—pad with null bytes if it's too short, truncate if it's too long.
4. Output Format Defaults Are Different
mcrypt outputs raw binary data by default, while openssl outputs Base64-encoded data by default. If your mcrypt code uses base64_encode() on the result, you need to either:
- Use
OPENSSL_RAW_DATAin openssl to get binary output, then applybase64_encode() - Or skip manual Base64 encoding and let openssl handle it (but only if your mcrypt code didn't do it)
5. You Might Be Using the Wrong Cipher Name
A common mistake: mcrypt's MCRYPT_RIJNDAEL_256 refers to 256-bit keys (AES-256), but openssl's rijndael-256 uses a 32-byte block size (not standard AES). You need to use aes-256-cbc in openssl to match AES-256-CBC from mcrypt.
Example Corrected Code
Let's say your original mcrypt code looks like this:
$key = "my_encryption_key"; $plaintext = "secret_data_to_encrypt"; $cipher = MCRYPT_RIJNDAEL_256; $mode = MCRYPT_MODE_CBC; // mcrypt uses full-null IV if not specified, so we'll replicate that $iv = str_repeat("\0", mcrypt_get_iv_size($cipher, $mode)); $encrypted_mcrypt = mcrypt_encrypt($cipher, $key, $plaintext, $mode, $iv); $encrypted_mcrypt_base64 = base64_encode($encrypted_mcrypt);
Here's the matching openssl version:
$key = "my_encryption_key"; // Adjust key to 32 bytes (match mcrypt's auto-padding/truncation) $key = str_pad(substr($key, 0, 32), 32, "\0"); $plaintext = "secret_data_to_encrypt"; $block_size = 16; // AES block size is always 16 bytes // Apply zero padding manually $padding_length = $block_size - (strlen($plaintext) % $block_size); $padded_plaintext = $plaintext . str_repeat("\0", $padding_length); // Use the same full-null IV as mcrypt $iv = str_repeat("\0", 16); // Encrypt with correct cipher, flags, and IV $encrypted_openssl = openssl_encrypt( $padded_plaintext, "aes-256-cbc", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv ); $encrypted_openssl_base64 = base64_encode($encrypted_openssl);
Now both should produce identical Base64-encoded results!
If you still have mismatches, double-check that every parameter (key, IV, plaintext, mode) is exactly the same across both functions.
内容的提问来源于stack exchange,提问作者Sos.

