WP: AesManaged encryption vs. mcrypt_encrypt

橙三吉。 提交于 2020-01-02 17:41:06

问题


I'm trying to synchronize my encryption and decryption methods between C# and PHP but something seems to be going wrong.

In the Windows Phone 7 SDK you can use AESManaged to encrypt your data

I use the following method:

    public static string EncryptA(string dataToEncrypt, string password, string salt)
    {
        AesManaged aes = null;
        MemoryStream memoryStream = null;
        CryptoStream cryptoStream = null;

        try
        {
            //Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator 
            Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));

            //Create AES algorithm with 256 bit key and 128-bit block size 
            aes = new AesManaged();
            aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
            aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // rfc2898.GetBytes(aes.BlockSize / 8);

            // to check my results against those of PHP
            var blaat1 = Convert.ToBase64String(aes.Key);
            var blaat2 = Convert.ToBase64String(aes.IV);

            //Create Memory and Crypto Streams 
            memoryStream = new MemoryStream();
            cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);

            //Encrypt Data 
            byte[] data = Encoding.Unicode.GetBytes(dataToEncrypt);
            cryptoStream.Write(data, 0, data.Length);
            cryptoStream.FlushFinalBlock();

            //Return Base 64 String 
            string result = Convert.ToBase64String(memoryStream.ToArray());

            return result;
        }
        finally
        {
            if (cryptoStream != null)
                cryptoStream.Close();

            if (memoryStream != null)
                memoryStream.Close();

            if (aes != null)
                aes.Clear();
        }
    }

I solved the problem of generating the Key. The Key and IV are similar as those on the PHP end. But then the final step in the encryption is going wrong.

here is my PHP code

<?php

function pbkdf2($p, $s, $c, $dk_len, $algo = 'sha1') {

    // experimentally determine h_len for the algorithm in question
     static $lengths;
     if (!isset($lengths[$algo])) { $lengths[$algo] = strlen(hash($algo, null, true)); }
    $h_len = $lengths[$algo];

    if ($dk_len > (pow(2, 32) - 1) * $h_len) {
         return false; // derived key is too long
     } else {
         $l = ceil($dk_len / $h_len); // number of derived key blocks to compute
         $t = null;
         for ($i = 1; $i <= $l; $i++) {
             $f = $u = hash_hmac($algo, $s . pack('N', $i), $p, true); // first iterate
             for ($j = 1; $j < $c; $j++) {
                 $f ^= ($u = hash_hmac($algo, $u, $p, true)); // xor each iterate
             }
             $t .= $f; // concatenate blocks of the derived key
         }
         return substr($t, 0, $dk_len); // return the derived key of correct length
     }

}


$password = 'test';
$salt = 'saltsalt';
$text = "texttoencrypt";

#$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
#echo $iv_size . '<br/>';
#$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
#print_r (mcrypt_list_algorithms());

$iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

$key = pbkdf2($password, $salt, 1000, 32);
echo 'key: ' . base64_encode($key) . '<br/>';
echo 'iv: ' . base64_encode($iv) . '<br/>';

echo '<br/><br/>';

function addpadding($string, $blocksize = 32){
     $len = strlen($string);
     $pad = $blocksize - ($len % $blocksize);
     $string .= str_repeat(chr($pad), $pad);
     return $string;
 }

echo 'text: ' . $text . '<br/>';
echo 'text: ' . addpadding($text) . '<br/>';

// -- works till here

$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);

echo '1.' . $crypttext . '<br/>';
$crypttext = base64_encode($crypttext);
echo '2.' . $crypttext . '<br/>';

$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, addpadding($text), MCRYPT_MODE_CBC, $iv);

echo '1.' . $crypttext . '<br/>';
$crypttext = base64_encode($crypttext);
echo '2.' . $crypttext . '<br/>';

?>

So to point out, the Key and IV look similar on both .NET and PHP, but something seems to be going wrong in the final call when executing mcrypt_encrypt(). The end result, the encrypted string, differs from .NET.

Can anybody tell me what i'm doing wrong. As far as i can see everything should be correct.

Thank you!

EDIT:

Additional information on the AESManaged object in .NET

Keysize = 256 Mode = CBC Padding = PKCS7


回答1:


MCRYPT_RIJNDAEL_256 is the version of the Rijndael algorithm with block size 256 bits, while AES is only the versions with block size 128 bits. These are not compatible.

Use MCRYPT_RIJNDAEL_128, to get an algorithm which is equivalent to AES. It still supports all three key sizes standardized as AES, i.e. 128 bits (16 bytes), 196 bits (24 bytes) and 256 bits (32 bytes). Simply pass a long enough string as key.




回答2:


I solved the problems. You need to make sure you use UTF8 Encoding on the strings and you are using the correct padding (PKCS7)

Here is the code:

    public static string EncryptA(string dataToEncrypt, string password, string salt)
    {
        AesManaged aes = null;
        MemoryStream memoryStream = null;
        CryptoStream cryptoStream = null;

        try
        {
            //Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator 
            Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));

            //Create AES algorithm with 256 bit key and 128-bit block size 
            aes = new AesManaged();
            aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
            aes.IV = Encoding.UTF8.GetBytes("AAAAAAAAAAAAAAAA"); // new byte[] { 0x41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // rfc2898.GetBytes(aes.BlockSize / 8);

            //Create Memory and Crypto Streams 
            memoryStream = new MemoryStream();
            cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write);

            //Encrypt Data 
            byte[] data = Encoding.UTF8.GetBytes(dataToEncrypt);
            cryptoStream.Write(data, 0, data.Length);
            cryptoStream.FlushFinalBlock();

            //Return Base 64 String 
            var temp = memoryStream.ToArray();
            string result = Convert.ToBase64String(temp);

            return result;
        }
        finally
        {
            if (cryptoStream != null)
                cryptoStream.Close();

            if (memoryStream != null)
                memoryStream.Close();

            if (aes != null)
                aes.Clear();
        }
    }

and in php:

<?php
function pbkdf2($p, $s, $c, $dk_len, $algo = 'sha1') {

    // experimentally determine h_len for the algorithm in question
     static $lengths;
     if (!isset($lengths[$algo])) { $lengths[$algo] = strlen(hash($algo, null, true)); }
    $h_len = $lengths[$algo];

    if ($dk_len > (pow(2, 32) - 1) * $h_len) {
         return false; // derived key is too long
     } else {
         $l = ceil($dk_len / $h_len); // number of derived key blocks to compute
         $t = null;
         for ($i = 1; $i <= $l; $i++) {
             $f = $u = hash_hmac($algo, $s . pack('N', $i), $p, true); // first iterate
             for ($j = 1; $j < $c; $j++) {
                 $f ^= ($u = hash_hmac($algo, $u, $p, true)); // xor each iterate
             }
             $t .= $f; // concatenate blocks of the derived key
         }
         return substr($t, 0, $dk_len); // return the derived key of correct length
     }
}

$text = "blaat";
$password = 'this is my secret passwordthis is my secret password';
$salt = 'thisismysalt';

#$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
#$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

$iv = 'AAAAAAAAAAAAAAAA';

$key = pbkdf2($password, $salt, 1000, 32);
echo 'key size: ' . strlen($key) . '<br/>';
echo 'key: ' . base64_encode($key) . '<br/>';
echo 'iv size: ' . strlen($iv) . '<br/>';
#echo 'iv: ' . $iv . '<br/>';
echo 'iv: ' . base64_encode($iv) . '<br/>';

echo '<br/><br/>';

#    $data = $this->paddingAlgorithm->padData($data, $blockSize);
#    return $iv . mcrypt_encrypt($this->MCRYPT_DES, $keyBytes, $data, MCRYPT_MODE_CBC, $iv);

function addpadding($string)
{
     $blocksize = 16;
     $len = strlen($string);
     $pad = $blocksize - ($len % $blocksize);
     $string .= str_repeat(chr($pad), $pad);

     return $string;
}

$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, addpadding($text), MCRYPT_MODE_CBC, $iv);
$crypttext = base64_encode($crypttext);
echo '3. [' . $crypttext . ']<br/>';

echo '<br/>';

?>

Hope this will help somebody as well.

Paulo thanks for your help!



来源:https://stackoverflow.com/questions/8116133/wp-aesmanaged-encryption-vs-mcrypt-encrypt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!