Шифрование TripleDES в C # и PHP не работает одинаково (PKCS7, ECB)?

Я потратил пару часов, пытаясь понять это, но никак не могу заставить это работать. У меня есть процедура шифрования C #, которую мне нужно сопоставить в php. Я не могу изменить версию C #, это не вариант (сторонняя сторона твердо придерживается этого мнения).

Вот код C #:

//In C#
// Console.WriteLine(ApiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl"));
// Results in: 
//     XvHbR/CsLTo=
public static string ApiEncode(string data, string secret)
{
  byte[] clear;

  var encoding = new UTF8Encoding();
  var md5 = new MD5CryptoServiceProvider();

  byte[] key = md5.ComputeHash(encoding.GetBytes(secret));

  TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
  des.Key = key;
  des.Mode = CipherMode.ECB;
  des.Padding = PaddingMode.PKCS7;

  byte[] input = encoding.GetBytes(data);
  try { clear = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); }
  finally
  {
    des.Clear();
    md5.Clear();
  }

  return Convert.ToBase64String(clear);
}

Вот лучшее из того, что я придумал в PHP:

//In PHP
// echo apiEncode("testing", "56dsfkj3kj23asdf83kseegflkj43458afdl");
// Results in: 
//    5aqvY6q1T54=
function apiEncode($data, $secret)
{    
  //Generate a key from a hash
  $key = md5(utf8_encode($secret), true);
  //Create init vector  
  $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ecb), MCRYPT_RAND); 

  //Pad for PKCS7
  $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
  $len = strlen($data);
  $pad = $blockSize - ($len % $blockSize);
  $data .= str_repeat(chr($pad), $pad);

  //Encrypt data
  $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb'); //, $iv);
  return base64_encode($encData);
}

Насколько мне известно, я правильно обрабатываю заполнение PKCS7 на стороне PHP. Я не знаю, что еще попробовать.

Следует отметить, что C # работает в Windows, а PHP - в Linux, и я не уверен, что это должно иметь значение.


person Redth    schedule 14.01.2011    source источник
comment
Может быть, это другая кодировка символов - это явно utf-8 в С #, но что это такое в php? каковы результаты вызова md5 на обоих языках.   -  person time4tea    schedule 14.01.2011
comment
Возможный дубликат тройного шифрования, не дающий одинаковых результатов в PHP и C #.   -  person netcoder    schedule 14.01.2011
comment
@netcoder - Не совсем тот же ответ, поэтому я бы сказал, не дубликат.   -  person erickson    schedule 14.01.2011
comment
@ time4tea - поскольку эти конкретные строки содержат только символы ASCII, это, вероятно, не будет иметь значения; единственная известная мне кодировка, которая не кодирует эти символы так же, как UTF-8, - это EBCDIC.   -  person erickson    schedule 14.01.2011
comment
Я увидел другой вопрос, и я попытался с ним работать, но я все еще не могу заставить его работать правильно, поэтому я разместил еще один вопрос ..   -  person Redth    schedule 14.01.2011
comment
Да, я пробовал явно использовать utf8_encode, что не имеет значения :(   -  person Redth    schedule 14.01.2011
comment
Если я это сделаю: $ strArr = str_split ($ key); foreach ($ strArr как $ strChar) echo (. ord ($ strChar)); Я получаю: 77 173 222 29 122 78 74 3 80 240 63 130 5 137 93 188 Что идентично байтам в моем ключе byte [] С # после вычисления хеша md5. Ключи такие же.   -  person Redth    schedule 14.01.2011


Ответы (4)


Длина заполнения в вашей версии PHP зависит от длины пароля. Это неверно. Вместо этого он должен основываться на длине вашего сообщения.

Попробуйте заменить strlen($password) на strlen($data).


Вторая проблема заключается в том, что для библиотеки mcrypt требуются 24-байтовые ключи. Тройной DES применяет обычный DES три раза, поэтому вы можете вызвать 8-байтовый ключ, используемый в каждом раунде DES, K 1, K 2 и K 3 < / sub>. Эти ключи можно выбрать разными способами. Самый безопасный - выбрать три разных ключа. Другой способ - установить K 3 равным K 1. Наименее безопасный метод (эквивалент DES) - сделать K 1 = K 2 = K 3.

Большинство библиотек достаточно «умны», чтобы интерпретировать 16-байтовый ключ 3DES как второй вариант выше: K 3 = K 1. Реализация .NET делает это за вас, а библиотека mcrypt - нет; вместо этого он устанавливает K 3 = 0. Вам нужно будет исправить это самостоятельно и передать mcrypt 24-байтовый ключ.

После вычисления хэша MD5 возьмите первые 8 байтов $key и добавьте их в конец $key, чтобы получить 24-байтовое значение для передачи mcrypt_encrypt().

person erickson    schedule 14.01.2011
comment
Исправлен пример кода, приведенный выше, и мой код, который я тестировал ... Я до сих пор не получаю одинаковую зашифрованную строку от обоих, так что, по-видимому, это не так ... - person Redth; 14.01.2011
comment
@Redth - Что вы теперь получаете в результате? Вы по-прежнему получаете 2 блока зашифрованного текста (24 символа), когда у вас должен быть только один (12 символов)? - person erickson; 14.01.2011
comment
@erickson нет, вы правы, теперь я получаю один блок зашифрованного текста: 5aqvY6q1T54 = это мой результат base64_encode ($ encData) теперь из PHP - person Redth; 14.01.2011
comment
@Redth - Хм. Поскольку вы используете режим ECB, IV не требуется. Возможно, его присутствие мешает правильному шифрованию. Есть ли другая версия функции mcrypt_encrypt(), для которой не требуется параметр iv, или вы можете передать null, 0 или любой другой PHP-эквивалент undefined вместо генерации IV? - person erickson; 14.01.2011
comment
Я думаю, что с md5 все еще есть проблема. Если я переключу C # на: byte [] key = encoding.GetBytes (ShHhd8a08JhJiho98ayslcjh); и переключите php на: $ key = ShHhd8a08JhJiho98ayslcjh; тогда мое шифрование работает. Итак, что-то не так с хешированием md5, которое различается между системами, что странно, поскольку я получаю одинаковые байты на ключах, когда я их проверяю ... - person Redth; 14.01.2011
comment
@erickson mcrypt_encrypt () не требует указания $ iv, но в этом случае использует \ 0. Мой результат будет таким же, независимо от того, указываю я IV или нет ... - person Redth; 14.01.2011
comment
более интересно ... если я вызову mcrypte_get_key_size ('tripledes', 'ecb'), я верну 24. Мой ключ - 16, поэтому он дополняется \ 0 в соответствии с документацией, чтобы заполнить 24. Теперь на стороне C # мой размер ключа отображается как 16 ... может быть, здесь что-то смешное? - person Redth; 14.01.2011
comment
да, вот и все ... Если я изменю свой c # на использование байтового [24] ключа, затем скопирую в него 16 байтов из хэша md5 и использую 24-байтовый ключ, он работает ... Теперь, поскольку я могу Не меняйте исходный код c #, как мне заставить php не использовать размер ключа 24 байта? - person Redth; 15.01.2011
comment
@Redth - Хорошо, в этом есть смысл. mcrypt не обрабатывает различные варианты ключей, поддерживаемые 3DES, как это делает большинство библиотек. Я изменил свой ответ, чтобы объяснить, как это обойти. - person erickson; 15.01.2011
comment
@erickson Я пробовал просто сделать $ key. = substr ($ key, 0, 8); который, похоже, тоже не работает ... есть пример? - person Redth; 15.01.2011
comment
похоже, что C # не берет первые 8 байтов и не добавляет их к ключу. Если я сделаю это вручную, то моя модификация php будет соответствовать, но, опять же, я не могу изменить файл c #. Интересно, что делает тогда С # в случае, если я предоставляю только 16 байтов ... Думаю, дело в том, как он заполняет остальные 24 байта ... - person Redth; 15.01.2011
comment
@Redth - Хм, у других работает; см. комментарии в руководстве по mcrypt. Но похоже, что вы делают то же самое с плохими результатами. - person erickson; 15.01.2011
comment
@erickson, может быть, для режима ECB они делают это по-другому в .NET? Как я уже упоминал, он работает, если я принудительно использую 24-байтовый ключ в .NET (добавляя первые 8 байтов в конец исходного 16-байтового ключа). Но это не работает. Если я просто использую 16-байтовый ключ в .NET, а затем на php снова добавляю первые 8 байтов ключа к себе ... - person Redth; 15.01.2011
comment
@Redth - Я сам тестировал (на Java) и получаю те же результаты, что и ваш .NET, при использовании 16-байтового ключа. Кроме того, если я создаю 24-байтовый ключ, как описано (устанавливая последние 8 байтов равными первым 8 байтам), я получаю тот же результат зашифрованного текста (мои результаты не меняются). Похоже, вы говорите, что ваш .NET-код дает другой результат, если вы сами создаете 24-байтовый ключ. Что же тогда за шифротекст? И, когда вы создаете 24-байтовый ключ в PHP, каков ваш зашифрованный текст? - person erickson; 15.01.2011
comment
@erickson, ты прав. Я забыл изменить md5 (); вернуться к md5 (, правда); после некоторого тестирования я делал .. Теперь все работает! Большое спасибо за Вашу помощь! - person Redth; 15.01.2011

Я нашел решение, проверьте эту ссылку, может вам помочь. http://sanity-free.com/131/triple_des_between_php_and_csharp.html

А вот функция дешифрования на всякий случай:

    public static string Decrypt(string cypherString)
    {

        byte[] key = Encoding.ASCII.GetBytes("icatalogDR0wSS@P6660juht");
        byte[] iv = Encoding.ASCII.GetBytes("iCatalog");
        byte[] data = Convert.FromBase64String(cypherString);
        byte[] enc = new byte[0];
        TripleDES tdes = TripleDES.Create();
        tdes.IV = iv;
        tdes.Key = key;
        tdes.Mode = CipherMode.CBC;
        tdes.Padding = PaddingMode.Zeros;
        ICryptoTransform ict = tdes.CreateDecryptor();
        enc = ict.TransformFinalBlock(data, 0, data.Length);
        return UTF8Encoding.UTF8.GetString(enc, 0, enc.Length);
    }
person Sebastian    schedule 31.01.2012

Взгляните на encoding.getBytes, вам нужны байты секретного ключа из UTF8 ...

person Stefan Steiger    schedule 14.01.2011

Похоже, что версия C # не устанавливает IV. Это может быть проблемой, если вы не знаете, что это, потому что msdn говорит:

Свойству IV автоматически присваивается новое случайное значение всякий раз, когда вы создаете новый экземпляр одного из классов SymmetricAlgorithm или когда вы вручную вызываете метод GenerateIV.

Похоже, что в версии PHP вы используете IV. Вы можете попробовать не указывать IV и надеяться, что версия C # также использует нули.

Изменить: похоже, для ЕЦБ IV игнорируется.

Вам также может потребоваться кодировать ключ, как в версии C #, с помощью utf8-encode

person SwDevMan81    schedule 14.01.2011
comment
Это режим ЕЦБ. Нет IV. - person erickson; 14.01.2011