C # & lt; — & gt; PHP динамический обмен ключами AES

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

Код 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;
}

3

Решение

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


Чтобы PKCS # 7-pad открытый текст перед шифрованием, используйте:

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

чтобы распаковать открытый текст после дешифрования, просто выполните:

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

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

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]