繁体   English   中英

使用C#匹配PHP AES 128 ECB加密

[英]Matching PHP AES 128 ECB encryption with C#

我有以下要尝试在C#中模仿的PHP示例。 它使用带有PKCS7填充的AES 128位ECB加密:

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

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

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

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

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

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

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

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

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

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

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

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

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

我目前正在运行以下C#代码以尝试模拟结果:

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();
}

当我查看这些结果时,得到的是:

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!

那里有很多诊断代码和杂项代码,但是基本的问题是,当传入密钥的相同MD5哈希(二进制文件相同)并且传入相同的输入数据(字节相同)时,或者我可以在输入的C#代码中强制填充相同),以获得不同的输出结果。 我敢肯定这很简单,但是并没有突然出现。 这里有人可以识别问题吗?

基本问题是,底部显示的SID(AES加密结果)是不同的-造成差异的原因是什么?

主要问题是PHP代码中的实际键是由md5()创建的,而没有可选参数来获取原始输出。 这意味着实际密钥的长度为32个字节,因为它是十六进制编码的,并且按原样使用而无需解码。 本质上,您在这里处理的是AES-256。

由于您无法更改PHP代码,因此需要在C#中重新创建此事故:

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

预期输出:

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

BitConverter.ToString()返回形式为“ 6D-33-42-01 -....”的十六进制字符串,这意味着必须删除破折号,并且必须将其转换为小写字母以创建实际的密钥。

DEMO

那么,可以帮助缩小问题范围的第一件事是“密钥的MD5哈希(原始):”在两种情况下都是相同的,但是base64编码与“密钥的MD5哈希(base64):”不匹配,因此缩小了问题扩散到base64call的代码的步骤。

请注意,您输入的是十六进制字符串,而不是PHP中的二进制。

使用专门针对十六进制字符串到base64的在线转换器进行测试,我可以验证bTNCAct2JTI9oy4MMbKxOA==是正确的base64编码,对于与C#输出匹配的0x6d334201cb7625323da32e0c31b2b138输入。

因此,这意味着PHP的base64编码是罪魁祸首。 也许有更好的方法来检索二进制密钥,但是您需要做的是将十六进制字符串转换为二进制:

<?php
echo base64_encode(pack("H*" , '6d334201cb7625323da32e0c31b2b138')) ;
?>

这将产生bTNCAct2JTI9oy4MMbKxOA==的预期输出

问题可能不止于此,但这可以解释产出的差异。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM