PHP与C#实现AES-128-CBC加密解密兼容问题解决
PHP与C# AES-128-CBC加密兼容问题解决
问题背景与代码
PHP加密代码
$cipher = "AES-128-CBC"; $key = openssl_digest("OfVb3CjsZhnX81ZzjfAHuIiMtKEUyrt6", 'SHA256', TRUE); $ivlen = openssl_cipher_iv_length($cipher); $iv = openssl_random_pseudo_bytes($ivlen); $ciphertext_raw = openssl_encrypt("wsUser", $cipher, $key, OPENSSL_RAW_DATA, $iv); $hmac = hash_hmac('sha256', $ciphertext_raw, $key, true); $result = base64_encode($iv . $hmac . $ciphertext_raw); // $result value will decode by http_build_query // $request_query["Key"] = $result; // $last_query = http_build_query($request_query);
初始转换的C#代码
var cipher = "AES-128-CBC-----"; var key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes("OfVb3CjsZhnX81ZzjfAHuIiMtKEUyrt6")); var ivlen = cipher.Length; byte[] iv = new byte[16]; Random rnd = new Random(); rnd.NextBytes(iv); var aes = Aes.Create("AesManaged"); // 初始代码未添加以下密钥截取逻辑 //C# 4.5 //byte[] m = new byte[16]; //Array.Copy(key, 0, m, 0, 16); //aes.Key = m; //C# 8.0 //aes.Key = key[0..16]; aes.IV = iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; var encryptor = aes.CreateEncryptor(aes.Key,iv); var ciphertextRaw = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(txtwsuser.Text), 0, txtwsuser.Text.Length); // 明文为"wsUser" var hmac = new HMACSHA256(key).ComputeHash(ciphertextRaw); string cw = Convert.ToBase64String(iv.Concat(hmac).Concat(ciphertextRaw).ToArray()); textBox1.Text = HttpUtility.UrlEncode(cw).Replace("%3d", "%3D"); // 调试用代码
PHP解密代码
$encrypted_value = urldecode("data_from_client"); // 可填入textBox1.Text的值 $cipher = "AES-128-CBC"; $key = openssl_digest(wsKey, 'SHA256', TRUE); $cw = base64_decode($encrypted_value); $ivlen = openssl_cipher_iv_length($cipher); $iv = substr($cw, 0, $ivlen); $hmac = substr($cw, $ivlen, $sha2len=32); $cw3 = substr($cw, $ivlen+$sha2len); $calcmac = hash_hmac('SHA256', $cw3, $key, true); $result = openssl_decrypt($cw3, $cipher, $key, OPENSSL_RAW_DATA, $iv);
问题现象
- PHP解密器能正常解密自身生成的加密值,但无法解密C#生成的内容
- 已确认双方密钥和HMAC哈希值一致
- C#生成的加密值:
sCn1%2bY2Kx9RzIiDIsWL1xWRxecMO%2bkPhIdkBfXbV6uD7%2by75f3EvwpyP4e3CHLDjBZ7U0CrVDsK4MdBUn25K0Q%3D%3D - PHP生成的加密值(可正常解密):
%2FUMZJmd2LfIJzJntETKcgo%2F7VDaBV8U9iLY%2Bopq2Up5%2BrtOLRMxR%2B3OzbzgLEx0ZZErjZFP8oOtMmnZ%2FEX1dxA%3D%3D
解决方案
在C#代码中添加密钥截取逻辑,仅取SHA256生成的32字节密钥的前16字节作为AES-128的密钥:
修改后的C#密钥处理代码
// C# 4.5 版本写法 byte[] m = new byte[16]; Array.Copy(key, 0, m, 0, 16); aes.Key = m; // C# 8.0+ 版本写法 aes.Key = key[0..16];
原因说明
AES-128要求密钥长度为16字节:
- PHP中
openssl_encrypt使用AES-128-CBC模式时,会自动截取32字节SHA256哈希的前16字节作为密钥 - C#中若直接将32字节哈希赋值给
aes.Key,会被识别为AES-256密钥,导致加密模式不匹配,最终PHP解密失败
手动截取前16字节后,双方使用的密钥长度一致,加密逻辑匹配,即可解决兼容性问题。
内容的提问来源于stack exchange,提问作者Precision Tillage
相关产品推荐
相关产品推荐

