我正在开发一个安全文件传输项目,该项目在客户端使用 C# 客户端对文件进行加密。我需要使用 php 或者 phpseclib 在服务器端解密文件。这里的代码是我从 msdn 示例中复制的。但我无法计算出 php 中的解密函数。
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
byte[] saltBytes = passwordBytes;
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.KeySize = 256;
AES.BlockSize = 256;
AES.Mode = CipherMode.CBC;
AES.Padding = PaddingMode.Zeros;
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
using (CryptoStream cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
这是不起作用的 php 代码:
$pw = "this_is_my_pw";
$aes = new Crypt_AES(CRYPT_AES_MODE_CBC);
$aes->setKey($pw);
$aes->setKeyLength(256);
$aes->disablePadding();
$file = "enc.txt";
$fh = fopen($file, "r");
$contents = trim(fread($fh, filesize($file)));
fclose($fh);
//echo "Encoded: \n\n" . $contents;
$contents = $aes->decrypt($contents);
#$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
#$padding = $block - (strlen($clear) % $block);
#$dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $pw, base64_decode($contents), MCRYPT_MODE_CBC, $pw);
echo "Decoded: \n\n" . $contents;
有人可以帮我解决这个问题或提示我做错了什么吗?
解密时不使用初始化向量。您需要将初始化向量 (IV) 与数据一起发送 - 您的 PHP 代码永远不会调用$aes->setIV
来自 phpseclib,因此它将永远无法解密文本,因为如果未设置 phpseclib 使用全零的 IV根据文档 http://phpseclib.sourceforge.net/crypt/examples.html。我个人建议使用 C# 生成安全随机 IVRijndaelManaged.GenerateIV https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.generateiv(v=vs.110).aspx,但显然从 PBKDF2 密钥导出 IV 被认为是可以接受的。 PBKDF2(在 RFC 2898 中指定)是密钥拉伸算法Rfc2898DeriveBytes
实施。无论如何,您需要在 PHP 端重新生成 IV,无论这意味着使用加密数据传输 IV(完全没问题),还是在 PHP 端重新导出 IV。
使用密码作为盐是一个非常糟糕的主意。盐需要有足够的长度并且以加密方式随机生成。使用密码作为盐完全失去了盐的意义。MSDN 有一些示例代码 https://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes(v=vs.110).aspx展示了如何结合使用生成加密随机盐Rfc2898DeriveBytes
,但重要的部分在这里:
byte[] saltBytes = new byte[8];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
-
盐必须与加密数据一起传输。您需要发送 PBKDF2 salt 字节以及 IV 字节和加密数据。 phpseclib 将需要所有这些来正确初始化自身并解密数据。您可能想使用 phpseclibsetPassword https://api.phpseclib.org/master/phpseclib/Crypt/Base.html#method_setPassword这样做,就像这样:
$salt = ...; // get the salt to your PHP code somehow
$iv = ...; // get the IV to your PHP code
$pw = "this_is_my_pw";
$aes = new Crypt_AES(CRYPT_AES_MODE_CBC);
$aes->setPassword($pw, 'pbkdf2' /* key extension algorithm */,
'sha1' /* hash algorithm */, $salt /* generated salt from C# */,
1000 /* number of iterations, must be same as C# code */,
256 / 8 /* key size in bytes, 256 bit key / 8 bits per byte */
);
$aes->setIV($iv);
-
请记住有关块大小的其他答案。128 位是标准 AES 块大小,因此请确保 C# 和 phpseclib 都可以在更大的块大小下正常运行,或者仅对两者使用 AES 标准。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)