У меня есть Шифрование 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+P3HvctA0yBduAuwPS4Ah3AB4Dbdj2KBGC1Dk4b8GEbtXs5fINzvusp8FRBknF15Br2xI1CqP0Qb/M4w
Я просто еще не получил, что я делаю неправильно.
У Вас есть какие-либо идеи?
Править: Один момент я думаю, является критическим: у Меня есть один Вектор инициализации в моем коде C#, 16 байтов. Этот IV не установлен в симметричном ключе SQL, я мог сделать это?
Но даже не заполняя IV в C#, я получаю совсем другие результаты, и в содержании и в длине.
Есть пара вещей, на которые я хотел бы обратить внимание:
Убедитесь, что открытый текст идентичен по содержанию и кодировке. IIRC, по умолчанию для потоков используется UTF-8, тогда как если ваша функция VarBinaryToBase64
принимает параметр nvarchar, это будет Unicode.
Убедитесь, что оба алгоритма шифрования используют одинаковый размер блока. В SQL вы определяете алгоритм, когда вызываете CREATE SYMMETRIC KEY
. Если вы не укажете алгоритм, он будет использовать AES256. В .NET, использующем RijndaelManaged
, я считаю, что размер блока по умолчанию равен 128, но вы можете установить его равным 256 (вы не можете, если используете класс Aes
).
Последнее, что я бы искал, - это то, как SQL Server работает с векторами инициализации, как вы упомянули в своем измененном сообщении. Я хочу сказать, что для этого используется параметр Authenticator
, но это дикая догадка.
РЕДАКТИРОВАТЬ
Я был далеко. Учитывая то, что я обнаружил, вы не можете использовать класс .NET для расшифровки текста, зашифрованного встроенным шифрованием SQL Server, потому что SQL Server добавляет к тому, что зашифровано, кучу слизи, включая случайный вектор инициализации. Из книги Майкла Коула «Руководство программиста Pro T-SQL 2005» (хотя в 2008 году это делается точно так же):
Когда SQL Server шифрует симметричным ключом , он добавляет метаданные к зашифрованному result, а также заполнение, в результате чего зашифрованный результат становится больше (иногда значительно больше), чем незашифрованный простой текст . Формат для зашифрованного результата с метаданными следует этому формату:
- Первые 16 байтов зашифрованного результата представляют собой GUID симметричного ключа , используемого для шифрования данных.
- Следующие 4 байта представляют номер версии, в настоящее время жестко закодированный как «01000000».
- Следующие 8 байтов для шифрования DES (16 байтов для шифрования AES) представляют собой случайно сгенерированный вектор инициализации .
- Следующие 8 байтов представляют собой информацию заголовка, представляющую параметры , используемые для шифрования данных. Если используется опция аутентификатора , эта информация заголовка включает 20-байтовый хэш SHA1 аутентификатора, что делает информацию заголовка 28 байтами в {{ 1}} длина.
- Последняя часть зашифрованных данных - это фактические данные и сами заполнители. Для алгоритмов DES длина этих зашифрованных данных будет кратна 8 байтам. Для алгоритмов AES длина будет кратной 16 байтам.
Не совсем ответ, но слишком большой для комментария. Возможно, это поможет кому-то еще разобраться :)
SQL Server, похоже, делает большое количество прокладок. Когда я пробовал ваш пример, я получал разные результаты, хотя первые 18 символов, похоже, каждый раз были одинаковыми. Однако это пока не имеет для меня смысла...
Вы не показываете, как вызывается RijndaelEncrypt
, но из-за разной длины данных я подумал о различиях между кодовыми страницами unicode и ANSI. Однако, похоже, вы не используете юникод в SQL Server, так что разница в длине будет наоборот, если что.