У меня есть следующий пример PHP, который я пытаюсь имитировать в C #. Используется 128-битное шифрование AES AES с дополнением PKCS7:
$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), отличается — что вызывает разницу?
Ну, первое, что могло бы помочь сузить проблему, это то, что «хэш ключа md5 (raw):» идентичен в обоих случаях, но кодировка base64 не совпадает с хэшем ключа md5 (base64): », так что из сужает шаг, где проблема расходится с кодом для base64call.
Обратите внимание, что ваш ввод — это шестнадцатеричная строка, а не двоичная в PHP.
Тестируя с онлайн-конвертером, специально предназначенным для шестнадцатеричных строк в base64, я могу убедиться, что bTNCAct2JTI9oy4MMbKxOA==
правильная кодировка base64, для ввода 0x6d334201cb7625323da32e0c31b2b138
который соответствует выводу C #.
Так что это означает, что кодировка PHP base64 является виновником. Вероятно, есть лучший способ получить ключ в двоичном формате, но вам нужно преобразовать шестнадцатеричную строку в двоичную:
<?php
echo base64_encode(pack("H*" , '6d334201cb7625323da32e0c31b2b138')) ;
?>
Это дает ожидаемый результат bTNCAct2JTI9oy4MMbKxOA==
Может быть больше проблем, чем это, но это объясняет эту разницу в результатах.
Основная проблема заключается в том, что фактический ключ в коде PHP создается md5()
без необязательного аргумента, чтобы получить необработанный вывод. Это означает, что фактический ключ имеет длину 32 байта, поскольку он закодирован в шестнадцатеричном формате и используется как есть, без декодирования. По сути, вы имеете дело с AES-256 здесь.
Поскольку вы не можете изменить код PHP, вам нужно воссоздать эту неудачу в C #:
aes.KeySize = 256;
aes.Key = Encoding.ASCII.GetBytes(BitConverter.ToString(md5HashOfKey).Replace("-","").ToLower());
Вывод как ожидалось:
mcrypt (необработанный): B0D298C83D20A0CBB9F0EA4305CEF2EC mcrypt: Ҙ = ˹ C presid: sNKYyD0goMu58OpDBc7y7A == sid: sNKYyD0goMu58OpDBc7y7A ==
BitConverter.ToString()
возвращает шестнадцатеричную строку в форме «6D-33-42-01 -….», что означает, что тире должны быть удалены и должны быть преобразованы в нижний регистр для создания фактического ключа.