Rijndael/AES解密c#到PHP的转换


Rijndael/AES decryption C# to PHP conversion

我在c#中有以下代码

string s = "hellowld";
byte[] bytes = new UnicodeEncoding().GetBytes(s);
FileStream stream = new FileStream(inputFile, FileMode.Open);
RijndaelManaged managed = new RijndaelManaged();
CryptoStream stream2 = new CryptoStream(stream, managed.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
FileStream stream3 = new FileStream(outputFile, FileMode.Create);
try
{
    int num;
    while ((num = stream2.ReadByte()) != -1)
    {
        stream3.WriteByte((byte) num);
    }
 [....]

此代码段解密某个文件并输出解密后的版本。在RijndaelManaged的CreateDecryptor方法中,我使用密码作为KEY和IV。

我在这里找到了一些PHP的stackoverflow代码,但如果我试图给键和iv相同的字节数组,就像c#一样,什么也不会发生。

$Pass = "hellowld";
$Clear = file_get_contents('./file.dat', FILE_USE_INCLUDE_PATH);
$bytePass=array();
$i = 0;
foreach (str_split($Pass) as $value) {
    $bytePass[$i]=ord($value);
    $i++;
}
echo decryptAES($Clear,$bytePass,$bytePass);
function decryptAES($content,$iv, $key,$aes) {
// Setzt den Algorithmus
switch ($aes) {
    case 128:
       $rijndael = 'rijndael-128';
       break;
    case 192:
       $rijndael = 'rijndael-192';
       break;
    default:
       $rijndael = 'rijndael-256';
}
// Setzt den Verschlüsselungsalgorithmus
// und setzt den Output Feedback (OFB) Modus
$cp = mcrypt_module_open($rijndael, '', 'ofb', '');
 // Ermittelt die Anzahl der Bits, welche die Schlüssellänge des Keys festlegen
$ks = mcrypt_enc_get_key_size($cp);
// Erstellt den Schlüssel, der für die Verschlüsselung genutzt wird
$key = substr(md5($key), 0, $ks);
// Initialisiert die Verschlüsselung
mcrypt_generic_init($cp, $key, $iv);
// Entschlüsselt die Daten
$decrypted = mdecrypt_generic($cp, $content);
// Beendet die Verschlüsselung
mcrypt_generic_deinit($cp);
// Schließt das Modul
mcrypt_module_close($cp);
return trim($decrypted);
}

我真的需要一些帮助如何在PHP中正确创建代码。我没有必要在PHP中输出一个文件,一个字符串就足够了。

:默认的c# RijndaelManaged密码方法是AES-128-CBC。我将PHP代码更改为mcrypt模块(默认c#密码方法)

更新2:

我确实设法创建了一个Java解密器,这让我想到了另一件事。PHP必须使用PKCS7填充

你的c#代码看起来不是很安全,所以如果你可以改变它,看看下面的一些提示。下面是修改后的PHP代码,使其看起来与c#代码相当。

function decryptAES128CBC($content,$iv, $key) {
    // AES is Rijndael-128
    $rijndael = 'rijndael-128';
    // key size is 128 bit = 16 bytes
    $ks = 16;
    // CBC mode, not OFB
    $cp = mcrypt_module_open($rijndael, '', 'cbc', '');
    // pad key and IV by zeros (this is not a good idea)
    $key = str_pad($key, $ks, "'0");
    $iv = str_pad($key, $iv, "'0");
    // initialize the decryptor with key and IV
    mcrypt_generic_init($cp, $key, $iv);
    // the actual work
    $decrypted = mdecrypt_generic($cp, $content);
    // clean up
    mcrypt_generic_deinit($cp);
    mcrypt_module_close($cp);
    // remove padding, see below
    return unpad($decrypted);
}

最后一个unpad在那里删除填充,这可能是由加密函数附加的,以将消息大小扩大到完整的块数。RijndaelManaged使用的默认填充是PKCS7-padding,它附加一些字节(1到16之间),每个字节等于附加的字节数。在实际实现中,您将在解密后检查填充是否有效(即所有这些字节都具有相同的值),但对于"快速和肮脏",您可以简单地使用检查最后一个字节并删除许多字节的东西。示例请参见mcrypt_decrypt的注释。

如果你可以改变你的c#代码:

  • 请注意,使用固定值(每个密钥)作为初始化向量通常不是一个好主意,使用密钥本身作为初始化向量也不好。使用随机初始化向量(与消息一起发送),或参见下一点。

  • 此外,您通常不希望直接使用(相当短的)密码作为密钥,而是使用较长的密码短语,并使用盐(包含在消息中)对其进行散列以派生密钥。如果这样做,您还可以从相同的两个数据块派生初始化向量(但在某种程度上,它们是不同的,使用键派生函数)。为了避免从加密文件中强制使用密码,请在此处使用慢散列函数(PBKDF-2或bcrypt)。