duangan4070 2015-10-22 23:52
浏览 488

将PHP AES 128 ECB加密与C#匹配

I have the following PHP sample that I'm trying to mimic in C#. It is using AES 128bit ECB encryption with PKCS7 padding:

$trust_jsonString="hello";
echo "input: '" . $trust_jsonString . "'
";
echo "input (dump): " . var_dump($trust_jsonString) . "
";

$trust_key = "9840822c-14fc-49ac-9d68-ac532f9f171e";
echo "key: '" . $trust_key . "'
";

$blockSize=mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_ECB);
echo "block size: '" . $blockSize . "'
";

$padding = $blockSize - (strlen($trust_jsonString) % $blockSize);
echo "padding: '" . $padding . "'
";

$trust_jsonString .= str_repeat(chr($padding), $padding);

$trust_jsonString = utf8_encode($trust_jsonString);
echo "utf8 json: '" . $trust_jsonString . "'
";
echo "utf8 json (dump): " . var_dump($trust_jsonString) . "
";

$trust_key=utf8_encode($trust_key);
echo "encoded key: " . $trust_key . "
";

$trust_key=(md5($trust_key));
echo "md5 hash of key (raw): " . $trust_key . "
";

$mcrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $trust_key, $trust_jsonString, MCRYPT_MODE_ECB);
echo "mcrypt (raw): " . var_dump($mcrypt) . "
";
echo "mcrypt: " . $mcrypt . "
";

echo "mcrypt (raw): ";
$byte_array = byteStr2byteArray($mcrypt);
for($i=0;$i<count($byte_array);$i++)
{
   printf("%02x", $byte_array[$i]);
}
echo "
";

$presid = base64_encode($mcrypt);
echo "presid: " . $presid . "
";

$sid=strtr($presid,'+/', '-_');
echo "sid: " . $sid . "
";

function byteStr2byteArray($s) {
    return array_slice(unpack("C*", "\0".$s), 1);
}

I am currently running the following C# code to try to mimic results:

static void Main( string[] args )
{
    string data = "hello";
    Encrypt(data);
}

static void Encrypt( string data )
{
    PaddingMode padding = PaddingMode.PKCS7;
    CipherMode cipherMode = CipherMode.ECB;
    int size = 128;

    Console.WriteLine("input: '" + data + "'");

    string officialKey = "9840822c-14fc-49ac-9d68-ac532f9f171e";
    Console.WriteLine("key: '" + officialKey + "'");

    Console.WriteLine("block size: '16'");
    Console.WriteLine( "padding: '11'" );

    var utf8dataBytes = Encoding.UTF8.GetBytes(data);
    var utf8data = Encoding.UTF8.GetString(utf8dataBytes);
    Console.WriteLine("utf8 json: '" + utf8data + "'");

    Console.WriteLine("encoded key (utf8): " + officialKey);

    var utf8KeyBytes = Encoding.UTF8.GetBytes(officialKey);

    var myMD5 = MD5.Create();
    var md5HashOfKey = myMD5.ComputeHash(utf8KeyBytes);

    Console.WriteLine( "md5 hash of key (raw): " + DumpBinary(md5HashOfKey) );

    byte[] encryptedBlob;

    using ( var aes = new AesManaged() )
    {
        try
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.ECB;
            aes.KeySize = 128;
            aes.BlockSize = 128;
            aes.Key = md5HashOfKey;
            //aes.IV = new byte[] { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };

            var bytes = utf8dataBytes;
            //var bytes = ForcePaddingManually( utf8dataBytes );

            var cxform = aes.CreateEncryptor();
            encryptedBlob = cxform.TransformFinalBlock( bytes, 0, bytes.Length );
        }
        finally
        {
            aes.Clear();
        }
    }

    //var encryptedBlob = AesAlgo.Encrypt( data, md5HashOfKey, padding, cipherMode, size );
    Console.WriteLine("mcrypt (raw): " + DumpBinary(encryptedBlob));
    Console.WriteLine("mcrypt: " + Encoding.UTF8.GetString(encryptedBlob));

    var encryptedBase64 = Convert.ToBase64String(encryptedBlob);
    Console.WriteLine( "presid: " + encryptedBase64 );

    var encodedEncryptedBlob = encryptedBase64.Replace( "+", "-" ).Replace( "/", "_" );
    Console.WriteLine( "sid: " + encodedEncryptedBlob );
    Console.WriteLine("COMPLETE!");
}

static byte[] ForcePaddingManually( byte[] data )
{
    // force padding manually to test that PKCS7 works like we are expecting
    var pad = ( 16 - data.Length % 16 ) % 16;
    var bytes = new byte[data.Length + pad];
    for ( int i = 0; i < data.Length; ++i )
    {
        bytes[i] = data[i];
    }
    for ( int i = data.Length; i < data.Length + pad; ++i )
    {
        bytes[i] = (byte)pad;
    }

    return bytes;
}

static string DumpBinary( byte[] data )
{
    var sb = new StringBuilder();
    for ( int i = 0; i < data.Length; ++i )
    {
        sb.Append( data[i].ToString( "X2" ) );
    }
    return sb.ToString();
}

When I look at the results for these, this is what I get:

PHP:

input: 'hello'
string(5) "hello"
input (dump): 
key: '9840822c-14fc-49ac-9d68-ac532f9f171e'
block size: '16'
padding: '11'
utf8 json: 'hello'
string(16) "hello"
utf8 json (dump): 
encoded key: 9840822c-14fc-49ac-9d68-ac532f9f171e
md5 hash of key (raw): 6d334201cb7625323da32e0c31b2b138
string(16) "�Ҙ�= �˹��C���"
mcrypt (raw): 
mcrypt: �Ҙ�= �˹��C���
mcrypt (raw): b0d298c83d20a0cbb9f0ea4305cef2ec
presid: sNKYyD0goMu58OpDBc7y7A==
sid: sNKYyD0goMu58OpDBc7y7A==

C#:

input: 'hello'
key: '9840822c-14fc-49ac-9d68-ac532f9f171e'
block size: '16'
padding: '11'
utf8 json: 'hello'
encoded key (utf8): 9840822c-14fc-49ac-9d68-ac532f9f171e
md5 hash of key (raw): 6D334201CB7625323DA32E0C31B2B138
mcrypt (raw): 4CBAD7678AAB2B054371A1B572161280
mcrypt: L??g??+♣Cq??r▬↕?
presid: TLrXZ4qrKwVDcaG1chYSgA==
sid: TLrXZ4qrKwVDcaG1chYSgA==
COMPLETE!

There is a lot of diagnostic and misc code in there, but the basic issue is that when the same MD5 hash of the key is passed in (binaries are the same) and the same input data is passed in (bytes are the same, or I can force the padding to be the same in the C# code on input) I get different output results. I'm sure that this is something simple, but it isn't popping out to me. Is there someone who can identify the problem here?

The basic issue is that the SID shown at the bottom (the AES encrypted result) is different - what is causing the difference?

  • 写回答

2条回答 默认 最新

  • dongyo7931 2015-10-23 00:09
    关注

    The main problem is that the actual key in the PHP code is created by md5() without the optional argument to get the raw output. This means that the actual key is 32 bytes long, because it is hex encoded and it is used as-is without decoding. Essentially, you're dealing with AES-256 here.

    Since you can't change the PHP code, you need to re-create this mishap in C#:

    aes.KeySize = 256;
    aes.Key = Encoding.ASCII.GetBytes(BitConverter.ToString(md5HashOfKey).Replace("-","").ToLower());
    

    Output as expected:

    mcrypt (raw): B0D298C83D20A0CBB9F0EA4305CEF2EC
    mcrypt: �Ҙ�= �˹��C���
    presid: sNKYyD0goMu58OpDBc7y7A==
    sid: sNKYyD0goMu58OpDBc7y7A==
    

    BitConverter.ToString() returns a hex string of the form "6D-33-42-01-....", which means that the dashes must be removed and it must be converted to lowercase to create the actual key.

    DEMO

    评论

报告相同问题?

悬赏问题

  • ¥100 求数学坐标画圆以及直线的算法
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 自己瞎改改,结果现在又运行不了了
  • ¥15 链式存储应该如何解决
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站