如何将此 C# Rijndael 加密转换为 PHP?
已经有一些关于 SO 的有用问题:
There are already some helpful questions on SO:
- Rijndael 256 在 c# 和 php 之间加密/解密?
- 用 PHP 重写 Rijndael 256 C# 加密代码
- Rijndael/AES 解密 C# 到 PHP 的转换
但是,我的特殊情况仍然有困难.
However I am still having difficulties with my particular case.
我尝试了各种方法,但最终得到错误 IV 参数必须与块大小一样长"
或与生成的哈希不匹配的文本.
I've tried various methods but end up getting the error "The IV parameter must be as long as the blocksize"
or text that doesn't match the resulting hash.
我对加密的了解不够深入,无法弄清楚我做错了什么.
I don't understand encryption enough to work out what I'm doing wrong.
这是php版本:
$pass = 'hello';
$salt = 'application-salt';
echo Encrypt('hello', 'application-salt');
function Encrypt($pass, $salt)
{
$derived = PBKDF1($pass, $salt, 100, 16);
$key = bin2hex(substr($derived, 0, 8));
$iv = bin2hex(substr($derived, 8, 8));
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $pass, MCRYPT_MODE_CBC, $iv);
}
function PBKDF1($pass, $salt, $count, $dklen)
{
$t = $pass.$salt;
$t = sha1($t, true);
for($i=2; $i <= $count; $i++)
{
$t = sha1($t, true);
}
$t = substr($t,0,$dklen-1);
return $t;
}
还有 C# 版本:
Console.WriteLine(Encrypt("hello", "application-salt"));
// output: "Hk4he+qKGsO5BcL2HDtbkA=="
public static string Encrypt(string clearText, string Password)
{
byte[] clearData = System.Text.Encoding.Unicode.GetBytes(clearText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
MemoryStream ms = new MemoryStream();
Rijndael alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();
return Convert.ToBase64String(encryptedData);
}
我希望能够在一个新的基于 php 的应用程序中验证用户登录,该应用程序将与现有 C# 应用程序通信到同一个 MySQL 数据库.我打算加密密码并将生成的哈希值与存储在数据库中的哈希值进行比较以进行身份验证.
I want to be able to validate user logins in a new php-based application which will communicate to the same MySQL database as an existing C# application. I intend to encrypt the password and compare the resulting hash to the one stored in the database to authenticate.
任何指针将不胜感激.
我意识到在 C# 函数中,正在调用 PasswordDeriveBytes
并传递一个字节数组作为参数,我在 PHP 版本中没有类似的参数.我发现这源自 Codeproject 示例,并且ASCII 中的字节数组拼写为Ivan Medvedev",我假设他是示例作者.不幸的是,我无法改变这一点.
I realize that in the C# function, the PasswordDeriveBytes
is being called and passed a byte array as an argument for which I don't have an analog in the PHP version. I discovered that this originates from a Codeproject example and that the byte array in ASCII spells "Ivan Medvedev" whom I assume to be the example author. Unfortunately I cannot change this.
推荐答案
我认为 PHP 版本实际上可能会在键和 IV 中添加 00h 值字节.它们都有一个无效的大小:每个 8 个字节.对于 AES-128,它们需要扩展到 16 个字节.在您的 C# 代码中,您使用 32 个字节作为密钥,因此将使用具有 密钥大小 256 位的 AES.
I think that the PHP version may actually add 00h valued bytes to the key and IV. They both have an invalid size : 8 bytes for each. They need to be extended to 16 bytes for AES-128. In your C# code you use 32 bytes for the key, which will therefore use AES with a key size of 256 bits.
此外,您没有在 PasswordDeriveBytes
中指定迭代次数,您应该指定它,因为该类没有指定默认的迭代次数 - 根据您的评论,这将是 100,让我们假设它是.
Futhermore, you don't specify the number of iterations in PasswordDeriveBytes
, you should specify it as the class does not specify the default number of iterations - according to your comments, this would be 100, lets assume it is.
哦,您使用了不正确的加密方法.MCRYPT_RIJNDAEL_256 指定使用 blocksize 256 位的 Rijndael 算法,而不是 256 位的 密钥.据推测,密钥的位大小只是密钥的字节数乘以 8.
Oh, and you use the incorrect encryption method. MCRYPT_RIJNDAEL_256 specifies the Rijndael algorithm using a blocksize of 256 bits, not keys of 256 bits. Presumably, the bitsize of the keys is simply the number of bytes of the key times 8.
你能用这个替换你的加密功能然后再试一次吗?
Could you replace your Encrypt function with this and try again?
function Encrypt($pass, $salt)
{
$derived = PBKDF1($pass, $salt, 100, 48);
$key = bin2hex(substr($derived, 0, 32));
$iv = bin2hex(substr($derived, 32, 16));
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $pass, MCRYPT_MODE_CBC, $iv);
}
最后,请在进行加密或解密之前检查生成的IV和密钥是否与PHP中的IV和密钥匹配.你确定那个 PHP PBKDF1 函数是正确的?
Finally, please check if the generated IV and key match with the ones in PHP before performing encryption or decryption. Are you sure that that PHP PBKDF1 function is correct?
更新:这里是关于 M$ PBKDF1 例程的更多信息在 PasswordDeriveBytes 中(包括您可以尝试转换的 Java 代码):
UPDATE: Here is some more information on the M$ PBKDF1 routines in PasswordDeriveBytes (including Java code which you may try and convert):
哈,我明白你的意思.
有趣的是,使用.NET:调用48时结果不同或调用 32 然后调用 16:
Interestingly, using .NET: the results are different when calling 48 or calling 32 followed by 16:
.NET GetBytes( 32 +16 ):04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922655D8DD89AE1CAAC60A8041FCD7E8DA4
.NET GetBytes( 32 +16 ): 04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922655D8DD89AE1CAAC60A8041FCD7E8DA4
.NET GetBytes(32)04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922其次是 GetBytes( 16 ) 89946D3662B3196860A8041FCD7E8DA4
.NET GetBytes( 32 ) 04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922 Followed by GetBytes( 16 ) 89946D3662B3196860A8041FCD7E8DA4
真正的 Microsoft 代码,他们无法更改它,因为它可能会破坏该领域的应用程序.请注意,当使用 16 字节和 8 字节或直接使用 24 字节设计调用它时,它们也会返回不同的结果.您最好升级到 PBKDF2,并将 PBKDF1 限制为最大 20 个字节,如标准中所定义.
True Microsoft code, and they cannot change it because it could break applications in the field. Note that they also would return different results when calling it with 16 and then 8 bytes or directly by 24 bytes by design. You'd better upgrade to PBKDF2, and keep PBKDF1 limited to 20 bytes max, as defined in the standards.
相关文章