Портирование C# TripleDESCryptoServiceProvider шифрования на PHP
Есть проект, написанный на C#, который использует следующий код для шифрования / дешифрования данных в / из базы данных:
public string EncryptString(string Text)
{
byte[] IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
string Key = "abcdef";
byte[] buffer = Encoding.UTF8.GetBytes(Text);
TripleDESCryptoServiceProvider triple = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
triple.IV = IV;
triple.Key = md5.ComputeHash(ASCIIEncoding.UTF8.GetBytes(Key));
byte[] encodeText = triple.CreateEncryptor().TransformFinalBlock(buffer, 0, buffer.Length);
string user = Convert.ToBase64String(encodeText);
return user;
}
public static string DecryptString(string EncryptText)
{
byte[] IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
string Key = "abcdef";
byte[] decodeText = null;
byte[] buffer = Convert.FromBase64String(EncryptText);
TripleDESCryptoServiceProvider triple = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
triple.IV = IV;
triple.Key = md5.ComputeHash(ASCIIEncoding.UTF8.GetBytes(Key));
decodeText = triple.CreateDecryptor().TransformFinalBlock(buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(decodeText);
}
Теперь я должен сделать то же самое в PHP. До сих пор я подошел так близко, как следующий код:
define('CIPHER', 'tripledes');
define('MODE', 'cbc');
function Encrypt($data)
{
$iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
$key = 'abcdef';
$tripleKey = substr(md5($key), 0, mcrypt_get_key_size(CIPHER, MODE));
$encodedText = mcrypt_encrypt(CIPHER, $tripleKey, $data, MODE, $iv);
return base64_encode($encodedText);
}
function Decrypt($data)
{
$iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
$key = 'abcdef';
$tripleKey = substr(md5($key), 0, mcrypt_get_key_size(CIPHER, MODE));
$decodedText = mcrypt_decrypt(CIPHER, $tripleKey, base64_decode($data), MODE, $iv);
return $decodedText;
}
Но я не могу расшифровать строку, зашифрованную кодом C#! Я не эксперт по шифрованию, может кто-нибудь сказать мне, какой алгоритм шифрования и какой режим мне использовать?
1 ответ
Ваша главная проблема заключается в обработке ключа. Одна проблема тривиальна: в PHP md5
Функция по умолчанию возвращает шестнадцатеричный дамп хеша, в то время как вам нужно необработанное двоичное значение, чтобы соответствовать C# MD5CryptoServiceProvider.ComputeHash()
, Для этого просто добавьте один true
параметр для вызова PHP (md5(key, true)
).
Другая проблема немного сложнее: MD5 возвращает 128-битный хэш, в то время как 3DES использует 192-битный ключ (на самом деле, 168-битный ключ, а остальные - биты четности, но давайте здесь проигнорируем). Когда вы предоставляете слишком короткий ключ для реализации PHP, он дополняет ключ нулевым байтом; однако реализация 3DES в.NET/CSP, похоже, повторяет ключевые байты с самого начала (оборачивая ключ). Поэтому, чтобы соответствовать поведению.NET в PHP, вам нужно вручную обернуть ключ ($key . $key
).
И последняя проблема - это заполнение: PHP дополняет данные нулями, в то время как.NET по умолчанию использует заполнение PKCS#7. Если вам нужно правильно обрабатывать заполнение, вам нужно добавить его при шифровании и удалить при расшифровке.
define('CIPHER', 'tripledes');
define('MODE', 'cbc');
function Encrypt($data)
{
$iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
$key = 'abcdef';
// determine key bytes from the key text, using MD5 and wrapping around
$key = md5($key, true);
$key = $key . $key;
$tripleKey = substr($key, 0, mcrypt_get_key_size(CIPHER, MODE));
// add PKCS#7 padding
$blocksize = mcrypt_get_block_size(CIPHER, MODE);
$paddingSize = $blocksize - (strlen($data) % $blocksize);
$data .= str_repeat(chr($paddingSize), $paddingSize);
$encodedText = mcrypt_encrypt(CIPHER, $tripleKey, $data, MODE, $iv);
return base64_encode($encodedText);
}
function Decrypt($data)
{
$iv = chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8);
// determine key bytes from the key text, using MD5 and wrapping around
$key = 'abcdef';
$key = md5($key, true);
$key = $key . $key;
$tripleKey = substr($key, 0, mcrypt_get_key_size(CIPHER, MODE));
$decodedText = mcrypt_decrypt(CIPHER, $tripleKey, base64_decode($data), MODE, $iv);
// check and remove PKCS#7 padding
if (!$decodedText) return $decodedText;
$lastByte = ord($decodedText[strlen($decodedText) - 1]);
if ($lastByte == 0 || $lastByte > mcrypt_get_block_size(CIPHER, MODE)) return FALSE;
$paddingText = substr($decodedText, -$lastByte, $lastByte);
$decodedText = substr($decodedText, 0, -$lastByte);
if ($paddingText != str_repeat(chr($lastByte), $lastByte)) return FALSE;
return $decodedText;
}
Но, как уже сказал комментатор выше, это не очень хорошая криптографическая реализация по многим причинам. (И моя реализация твиков тоже не особо закалена.)