如何将此C#Rijndael加密转换为PHP?

关于SO已经有一些有用的问题了:

  • Rijndael 256在c#和php之间加密/解密?
  • 用PHP重写Rijndael 256 C#加密代码
  • Rijndael / AES解密C#到PHP的转换

但是我的特殊情况仍然存在困难。

我尝试了各种方法,但最终得到错误"The IV parameter must be as long as the blocksize"或与结果散列不匹配的文本。

我不明白加密能够解决我做错的事情。

这是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的应用程序中validation用户登录,该应用程序将与现有的C#应用​​程序通信到同一个MySQL数据库。 我打算加密密码并将生成的哈希值与存储在数据库中的哈希值进行比较以进行身份​​validation。

任何指针都将非常受欢迎。

编辑:

我意识到在C#函数中,正在调用PasswordDeriveBytes并传递一个字节数组作为参数,我在PHP版本中没有模拟。 我发现这源于Codeproject示例,并且ASCII中的字节数组拼写了“Ivan Medvedev”,我认为它是示例作者。 不幸的是我无法改变这一点。

我认为PHP版本实际上可以将00h值的字节添加到密钥和IV。 它们都有一个无效的大小:每个8字节。 对于AES-128,它们需要扩展到16个字节。 在您的C#代码中,您使用32个字节作为密钥,因此将使用密钥大小为256位的AES。

此外,你没有指定PasswordDeriveBytes的迭代次数,你应该指定它,因为类没有指定默认的迭代次数 – 根据你的评论,这将是100,我们假设它是。

哦,你使用不正确的加密方法。 MCRYPT_RIJNDAEL_256使用256位的块大小来指定Rijndael算法,而不是256位的密钥 。 据推测,密钥的比特大小只是密钥的字节数乘以8。

你可以用这个替换你的加密function,然后再试一次吗?

 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中的匹配。 你确定PHP PBKDF1function是否正确?

更新: 以下是有关PasswordDeriveBytes中M $ PBKDF1例程的更多信息(包括您可以尝试和转换的Java代码):

哈,我明白你的意思了。

有趣的是,使用.NET:调用48或调用32后跟16时结果不同:

.NET GetBytes(32 +16):04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922655D8DD89AE1CAAC60A8041FCD7E8DA4

.NET GetBytes(32)04DD9D139DCB9DE889946D3662B319682159FF9C9B47FA15ED205C7CAF890922其次是GetBytes(16)89946D3662B3196860A8041FCD7E8DA4

真正的Microsoft代码,他们无法更改它,因为它可能会破坏现场的应用程序。 注意,当用16然后8字节或直接用24字节设计它们时,它们也会返回不同的结果。 您最好升级到PBKDF2,并将PBKDF1限制为最多20个字节,如标准中所定义。

以下代码段可能对寻找从C#到PHP的精确转换有所帮助

 mcrypt_cipher, $key, base64_decode($encrypted), $this->mcrypt_mode, $iv); } public function encrypt($key, $iv, $password) { $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB); $padding = $block - (strlen($password) % $block); $password .= str_repeat(chr($padding), $padding); return mcrypt_encrypt($this->mcrypt_cipher, $key, $password, $this->mcrypt_mode, $iv); } } $foo = new Foo; $pass = 'p@ss'; $salt = 's@1t'; $key = PBKDF1($pass, $salt, 2, 32); $iv = "@1B2c3D4e5F6g7H8"; $encrypted = $foo->encrypt($key,$iv,'test@123'); $encrypted = base64_encode($encrypted); echo 'Encrypted: '.$encrypted.''; echo 'Decrypted: '.$foo->decrypt($key, $iv, $encrypted); function PBKDF1($pass, $salt, $count, $cb) { static $base; static $extra; static $extracount= 0; static $hashno; static $state = 0; if ($state == 0) { $hashno = 0; $state = 1; $key = $pass . $salt; $base = sha1($key, true); for($i = 2; $i < $count; $i++) { $base = sha1($base, true); } } $result = ""; if ($extracount > 0) { $rlen = strlen($extra) - $extracount; if ($rlen >= $cb) { $result = substr($extra, $extracount, $cb); if ($rlen > $cb) { $extracount += $cb; } else { $extra = null; $extracount = 0; } return $result; } $result = substr($extra, $rlen, $rlen); } $current = ""; $clen = 0; $remain = $cb - strlen($result); while ($remain > $clen) { if ($hashno == 0) { $current = sha1($base, true); } else if ($hashno < 1000) { $n = sprintf("%d", $hashno); $tmp = $n . $base; $current .= sha1($tmp, true); } $hashno++; $clen = strlen($current); } // $current now holds at least as many bytes as we need $result .= substr($current, 0, $remain); // Save any left over bytes for any future requests if ($clen > $remain) { $extra = $current; $extracount = $remain; } return $result; } 

PHP已经在其Mcrypt模块中内置了此function。

试试这个: http : //www.php.net/manual/en/book.mcrypt.php