2014-12-21 3 views
3

Я хочу создать зашифрованную систему связи с обменом динамическим ключом между C# и PHP. На данный момент у меня есть рабочий шифр/расшифровка PHP < -> PHP и C# < -> C#, но он почему-то не работает PHP < -> C#. Проблема в том, что расшифрованная строка кода C# генерирует более длинный результат, чем ожидал PHP, но при просмотре вывода в виде простой строки данные одинаковы. Например, строка «daa», отправленная с C# на PHP, расшифрованная длина равна 28, что не является тем, что должно быть. То же самое со строкой отправленного из PHP в C#, я получаю ошибку компилятора ArgumentException: длинаC# <-> PHP динамический обмен ключами AES

C# код:

public static string EncryptTest(string input) 
{ 
    string key = "256 bit key (32 char)"; 

    input = Md5Sum(input).Substring(0, 4) + input; 

    var encoding = new UTF8Encoding(); 
    var Key = encoding.GetBytes(key); 
    byte[] encrypted; 
    byte[] result; 

    using (var rj = new RijndaelManaged()) 
    { 
     try 
     { 
      rj.Padding = PaddingMode.PKCS7; 
      rj.Mode = CipherMode.CBC; 
      rj.KeySize = 256; 
      rj.BlockSize = 256; 
      rj.Key = Key; 
      rj.GenerateIV(); 

      using (ICryptoTransform encryptor = rj.CreateEncryptor()) 
      { 
       using (MemoryStream ms = new MemoryStream()) 
       { 
        using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) 
        { 
         using (StreamWriter writer = new StreamWriter(cs)) 
         { 
          writer.Write(input); 
         } 
        } 

        encrypted = ms.ToArray(); 
        result = new byte[rj.BlockSize/8 + encrypted.Length]; 

        // Result is built as: IV (plain text) + Encrypted(data) 
        Array.Copy(rj.IV, result, rj.BlockSize/8); 
        Array.Copy(encrypted, 0, result, rj.BlockSize/8, encrypted.Length); 
       } 
      } 
     } 
     finally 
     { 
      rj.Clear(); 
     } 
    } 
    return Convert.ToBase64String(result); 
} 

public static string DecryptTest(string input) 
{ 
    string key = "256 bit key (32 char)"; 

    byte[] data = Convert.FromBase64String(input); 

    if (data.Length < 32) 
     return null; 

    var encoding = new UTF8Encoding(); 
    var Key = encoding.GetBytes(key); 

    using (RijndaelManaged aes = new RijndaelManaged()) 
    { 
     aes.Padding = PaddingMode.PKCS7; 
     aes.Mode = CipherMode.CBC; 
     aes.KeySize = 256; 
     aes.BlockSize = 256; 
     aes.Key = Key; 

     // Extract the IV from the data first. 
     byte[] iv = new byte[aes.BlockSize/8]; 
     Array.Copy(data, iv, iv.Length); 
     aes.IV = iv; 

     // The remainder of the data is the encrypted data we care about. 
     byte[] encryptedData = new byte[data.Length - iv.Length]; 
     Array.Copy(data, iv.Length, encryptedData, 0, encryptedData.Length); 

     using (ICryptoTransform decryptor = aes.CreateDecryptor()) 
     { 
      using (MemoryStream ms = new MemoryStream(encryptedData)) 
      { 
       using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) 
       { 
        using (StreamReader reader = new StreamReader(cs)) 
        { 
         string output = reader.ReadToEnd(); 

         if (output.Length < 4) 
          return null; 

         string dataHash = output.Substring(0, 4); 
         string dataInput = output.Substring(4); 
         string dataInputHash = Md5Sum(dataInput).Substring(0, 4); 

         if (dataHash != dataInputHash) 
          return null; 

         return dataInput; 
        } 
       } 
      } 
     } 
    } 
} 

private static string Md5Sum(string strToEncrypt) 
{ 
    UTF8Encoding ue = new UTF8Encoding(); 
    byte[] bytes = ue.GetBytes(strToEncrypt); 

    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); 
    byte[] hashBytes = md5.ComputeHash(bytes); 

    string hashString = ""; 

    for (int i = 0; i < hashBytes.Length; i++) 
    { 
     hashString += Convert.ToString(hashBytes[i], 16).PadLeft(2, '0'); 
    } 

    return hashString.PadLeft(32, '0'); 
} 

PHP код:

$key = "256 bit key (32 char)"; 

function iv() 
{ 
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); 
    return mcrypt_create_iv($iv_size, MCRYPT_RAND); 
} 
function encrypt($data, $key32) 
{ 
    # Prepend 4-chars data hash to the data itself for validation after decryption 
    $data = substr(md5($data), 0, 4).$data; 
    # Prepend $iv to decrypted data 
    $iv = iv(); 
    $enc = $iv.mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key32, $data, MCRYPT_MODE_CBC, $iv); 
    return base64_encode($enc); 
} 
function decrypt($data, $key32) 
{ 
    $data = base64_decode($data); 
    if ($data === false || strlen($data) < 32) 
     return null; 
    $iv = substr($data, 0, 32); 
    $encrypted = substr($data, 32); 
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key32, $encrypted, MCRYPT_MODE_CBC, $iv), "\0"); 
    if ($decrypted === false || is_null($decrypted) || strlen($decrypted) < 4) 
     return null; 
    $dataHash = substr($decrypted, 0, 4); 
    $data = substr($decrypted, 4); 
    if (substr(md5($data), 0, 4) !== $dataHash) 
     return null; // it breaks here, md5 sum is not correct because of the length 
    return $data; 
} 
+0

'DecryptTest' использует другой ключ, но я предполагаю, что это то, что вы забыли изменить. –

+0

Да, мой плохой. Все функции используют один и тот же 256-битный ключ. –

+0

Вы не используете AES на самом деле, вы используете Rijndael с размером блока 256 бит. С обеих сторон, однако, это не решение. –

ответ

2

PHP/Mcrypt не используя PKCS # 7 padding, он использует нулевое заполнение 0..n байтов, где n - размер блока.


Для PKCS # 7-падом открытого текста перед шифрованием, использование:

$pad = $blockSize - (strlen($data) % $blockSize); 
$pdata = $data . str_repeat(chr($pad), $pad); 

к unpad в открытый текст после дешифровки просто выполнить:

$pad = ord($pdata[strlen($pdata) - 1]); 
$data = substr($pdata, 0, strlen($pdata) - $pad); 

PKCS # 7 теперь специальный стандарт для заполнения. Нулевое заполнение не является детерминированным; он может изменить открытый текст, если открытый текст заканчивается нулем.

+0

Все комбинации работают сейчас, кроме PHP -> C# decrypt. Когда я пытаюсь расшифровать в C# строку, зашифрованную в PHP, я получаю следующее исключение: * CryptographicException: Bad PKCS7 padding. Недопустимая длина 126. Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException * Любая идея, почему? –

+0

Это должно быть размер блока в байтах, извините, пытаясь создать функцию с PHPDoc в среднем времени ... –

+0

Я использую это в PHP-шифровании для размера блока * $ blockSize = mcrypt_get_block_size (MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); * –

Смежные вопросы