Как получить совместимость между C # и SQL2k8 AES Encryption? - PullRequest
10 голосов
/ 13 мая 2010

У меня есть шифрование AES на двух столбцах: один из этих столбцов хранится в базе данных SQL Server 2000 ; другой хранится в базе данных SQL Server 2008 .

Поскольку база данных первого столбца (2000) не имеет встроенной функциональности для шифрования / дешифрования, мы решили использовать логику криптографии на уровне приложений с классами .NET для обоих.

Но так как база данных второго столбца (2008) допускает такую ​​функциональность, мы хотели бы ускорить перенос данных с использованием функций базы данных, поскольку перенос данных в SQL 2k намного меньше, чем в эту секунду, и это будет длиться более 50 часов из-за того, что сделано на уровне приложения.

Моя проблема началась с этого момента: с использованием того же ключа, я не достиг ни одного результата при шифровании значения, ни того же размера результата.

Ниже у нас есть полная логика с обеих сторон .. Конечно, я не показываю ключ, но все остальное то же самое:

private byte[] RijndaelEncrypt(byte[] clearData, byte[] Key)
{
    var memoryStream = new MemoryStream();

    Rijndael algorithm = Rijndael.Create();

    algorithm.Key = Key;
    algorithm.IV = InitializationVector;

    var criptoStream = new CryptoStream(memoryStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
    criptoStream.Write(clearData, 0, clearData.Length);
    criptoStream.Close();

    byte[] encryptedData = memoryStream.ToArray();
    return encryptedData;
}

private byte[] RijndaelDecrypt(byte[] cipherData, byte[] Key)
{
    var memoryStream = new MemoryStream();

    Rijndael algorithm = Rijndael.Create();

    algorithm.Key = Key;
    algorithm.IV = InitializationVector;

    var criptoStream = new CryptoStream(memoryStream, algorithm.CreateDecryptor(), CryptoStreamMode.Write);

    criptoStream.Write(cipherData, 0, cipherData.Length);

    criptoStream.Close();

    byte[] decryptedData = memoryStream.ToArray();

    return decryptedData;
}

Это пример кода SQL:

open symmetric key columnKey decryption by password = N'{pwd!!i_ll_not_show_it_here}'

declare @enc varchar(max)

set @enc = dbo.VarBinarytoBase64(EncryptByKey(Key_GUID('columnKey'), 'blablabla'))

select LEN(@enc), @enc

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

Результат в C #: eg0wgTeR3noWYgvdmpzTKijkdtTsdvnvKzh + uhyN3Lo =

Тот же результат в SQL2k8 выглядит следующим образом: AI0zI7D77EmqgTQrdgMBHAEAAACyACXb + P3HvctA0yBduAuwPS4Ah3AB4Dbdj2KBGC1Dk4b8GEbtXs5fINzvib5 * FFRF2F2RFFF

Я просто еще не понял, что я делаю неправильно.

У вас есть идеи?

РЕДАКТИРОВАТЬ: Одна вещь, я думаю, имеет решающее значение: у меня есть один вектор инициализации на мой код C #, 16 байтов. Этот IV не установлен на симметричном ключе SQL, могу ли я это сделать?

Но даже не заполняя IV в C #, я получаю очень разные результаты, как по содержанию, так и по длине.

Ответы [ 2 ]

6 голосов
/ 13 мая 2010

Я бы посмотрел пару вещей:

  1. Убедитесь, что открытый текст идентичен по содержанию и кодировке. IIRC, по умолчанию для потоков используется UTF-8, тогда как если ваша функция VarBinaryToBase64 принимает параметр nvarchar, это будет Unicode.

  2. Убедитесь, что оба алгоритма шифрования используют одинаковый размер блока. В SQL вы определяете алгоритм при вызове CREATE SYMMETRIC KEY. Если вы не укажете алгоритм, он использует AES256. В .NET, использующем RijndaelManaged, я считаю, что размер блока по умолчанию равен 128, но вы можете установить его равным 256 (нельзя, если вы используете класс Aes).

  3. Последнее, на что я обращаю внимание, - это как SQL Server работает с векторами инициализации, как вы упомянули в исправленном сообщении. Я хочу сказать, что для этого используется параметр authenticator, но это дикое предположение.

РЕДАКТИРОВАТЬ

Я был далеко. Учитывая то, что я обнаружил, вы не можете использовать класс .NET для дешифрования текста, зашифрованного встроенным шифрованием SQL Server, потому что SQL Server добавляет кучу сглаживания к тому, что шифруется, включая случайный вектор инициализации. Из книги Майкла Коула «Руководство программиста Pro T-SQL 2005» (хотя 2008 год делает то же самое):

Когда SQL Server шифрует симметрично ключ, он добавляет метаданные к зашифрованному результат, а также заполнение, делая зашифрованный результат больше (иногда значительно больше) чем незашифрованный простой текст. Формат для зашифрованный результат с метаданными следует этому формату:

  • Первые 16 байтов зашифрованного результата представляют GUID симметричный ключ, используемый для шифрования данных
  • Следующие 4 байта представляют номер версии, в настоящее время жестко закодированный как "01000000".
  • Следующие 8 байтов для шифрования DES (16 байтов для шифрования AES) представляют случайно сгенерированный вектор инициализации.
  • Следующие 8 байтов являются информацией заголовка, представляющей параметры используется для шифрования данных. Если опция аутентификатора используется, это Информация заголовка включает 20-байтовый SHA1 хеш аутентификатора, делающий заголовок информации 28 байтов в длина.
  • Последняя часть зашифрованных данных - это фактические данные и сами заполнители. Для алгоритмов DES длина этого зашифрованные данные будут кратны 8 байт. Для алгоритмов AES длина будет кратно 16 байтам.
1 голос
/ 13 мая 2010

Не совсем ответ, но слишком большой для комментария. Возможно, это поможет кому-то другому разобраться :)
SQL Server, кажется, делает много дополнений. Когда я попробовал ваш пример, я продолжал получать разные результаты, хотя первые 18 символов кажутся одинаковыми каждый раз. Это пока не имеет смысла для меня ...

Вы не показываете, как вызывается RijndaelEncrypt, но с разными длинами данных я думал о различиях между кодировкой Unicode и ANSI. Однако вы, похоже, не используете юникод в SQL Server, поэтому разница в длине будет наоборот, если что.

...