PHP中使用Hex密钥通过openssl_decrypt解密C# Rijndael加密串返回空的问题
解密返回空的核心原因
- 未处理加密结果的URL编码:C#代码最终输出的加密字符串经过
Uri.EscapeDataString编码,包含%2F、%3D这类URL转义字符,直接传入base64_decode会得到损坏的二进制数据,无法作为合法密文使用。 - 密钥处理逻辑完全错误:
- 提供的密钥是64位长度的十六进制字符串,对应二进制长度为32字节,匹配的AES算法是
aes-256-cbc,原代码里选的aes-128-cbc要求密钥长度为16字节,本身就存在长度不匹配的问题。 - 原代码通过
unpack('C*', $key)再拼接的方式,是把密钥字符串本身的ASCII值作为密钥使用,完全没有做十六进制到二进制的转换,生成的密钥长度超过60个字节,完全不符合AES算法的密钥长度要求,openssl会直接判定参数非法返回空。
- 提供的密钥是64位长度的十六进制字符串,对应二进制长度为32字节,匹配的AES算法是
- 未按C#的逻辑拆分IV:C#加密时将16字节的随机IV直接拼接在密文块的最前端,原代码把整个含IV的二进制块直接当成密文传给解密函数,既没有拆分出独立的IV,也没有在调用
openssl_decrypt时传入IV参数,CBC模式没有合法初始向量根本无法完成解密。 - 因为开启了严格的零填充校验模式,只要前面任意一步参数错误,openssl不会抛出具体错误,直接返回空字符串,放大了排查难度。
正确的解密实现
- 先对加密串做
urldecode还原成纯base64字符串,再做base64解码,得到“IV+密文”的拼接二进制块。 - 从拼接块的前16个字节截取作为CBC模式需要的IV,剩余部分为实际加密的密文内容。
- 将十六进制格式的密钥通过
hex2bin转成32字节的二进制密钥,匹配aes-256-cbc的要求。 - 调用openssl解密时传入正确的算法、密钥、拆分得到的IV,搭配
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING标记匹配C#的Zeros填充规则,最后移除解密结果末尾的零填充即可得到明文。
可运行的参考代码
function decrypt(string $encryptedString, string $hexKey): string { // 还原URL编码的base64字符串 $urlDecoded = urldecode($encryptedString); // 解码得到IV与密文的拼接二进制块 $rawBinary = base64_decode($urlDecoded); // 拆分前16字节为CBC模式需要的IV $iv = substr($rawBinary, 0, 16); $actualCipher = substr($rawBinary, 16); // 十六进制密钥转成匹配长度的二进制密钥 $binaryKey = hex2bin($hexKey); // 执行解密匹配C#的Zeros填充规则 $decrypted = openssl_decrypt( $actualCipher, 'aes-256-cbc', $binaryKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv ); // 移除末尾的零填充 return rtrim($decrypted, "\0"); } // 测试调用 $cipherText = "O0wTQerC%2FQs5sFUMc3uXoSkrsJcY7toWqlsQYRYAD64%3D"; $key = "2b8958eb1da990c039933a504259059fc9c8a893447fcbb3a8c504797e637288"; echo decrypt($cipherText, $key);
内容的提问来源于stack exchange,提问作者alakidolaki
相关产品推荐
相关产品推荐

