PHP中如何从openssl_encrypt获取匹配C# AES256-ECB的字节数组?
解决PHP与C# AES-256-ECB加密结果不一致的问题
你的问题核心在于填充方式不匹配,以及输出处理的差异,下面一步步帮你修正代码,得到和C#一致的结果:
问题根源分析
- 你的C#代码使用了
PaddingMode.Zeros,当明文长度不是AES块大小(16字节)的整数倍时,会填充0x00字节到块长度。 - PHP的
openssl_encrypt默认使用PKCS#7填充(填充的字节值等于填充的数量),这和C#的Zero填充逻辑完全不同,导致加密结果偏差。 - 另外,你原代码对加密结果做了Base64编码,但实际需要的是原始加密字节的十六进制表示,这也是结果不符的原因之一。
修正后的PHP代码
// 构造原始明文字节 $plaintext = chr(0x8F) . chr(0x21) . chr(0xFF) . chr(0x43) . chr(0x49) . chr(0x27); // AES块大小为16字节,计算需要填充的0字节数量 $blockSize = 16; $paddingLength = $blockSize - (strlen($plaintext) % $blockSize); // 手动填充0字节到块大小 $plaintextPadded = $plaintext . str_repeat(chr(0), $paddingLength); // 构造密钥字节 $key = chr(0xC8) . chr(0xAE) . chr(0x1D) . chr(0xEC) . chr(0xD8) . chr(0x25) . chr(0x8D) . chr(0x52) . chr(0x6F) . chr(0x56) . chr(0xFB) . chr(0x13) . chr(0xF1) . chr(0x5D) . chr(0x3C) . chr(0x84) . chr(0xE0) . chr(0x02) . chr(0x16) . chr(0x89) . chr(0x34) . chr(0x8F) . chr(0x32) . chr(0x87) . chr(0x10) . chr(0x0A) . chr(0xB6) . chr(0xCD) . chr(0x81) . chr(0x27) . chr(0xDE) . chr(0x7E); // 加密:使用AES-256-ECB,输出原始字节,指定不自动填充(我们已手动完成Zero填充) $encrypted = openssl_encrypt($plaintextPadded, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING); // 将加密后的原始字节转换为空格分隔的十六进制格式 $hexOutput = implode(' ', str_split(bin2hex($encrypted), 2)); echo $hexOutput; // 输出:F1 00 ED B4 25 8F A6 D5 13 32 4A 8E DE 87 0C 3F
关键说明
- 手动Zero填充:因为PHP的
openssl没有直接支持Zero填充的选项,所以我们需要自己计算填充长度,在明文末尾补充足够的0x00字节,使其长度为16字节的整数倍。 - 禁用自动填充:通过
OPENSSL_ZERO_PADDING标志告诉openssl_encrypt不要自动添加PKCS#7填充,避免重复填充导致结果错误。 - 输出转换:直接对加密后的原始字节使用
bin2hex转换,再格式化空格分隔,就能得到和C#一致的十六进制结果,不需要Base64编码。 - ECB模式的IV:ECB模式不需要初始化向量(IV),所以C#代码里设置IV的操作不影响最终结果,PHP里也不需要传递IV参数。
内容的提问来源于stack exchange,提问作者guenthernagel
相关产品推荐
相关产品推荐

