SQL Server: CLR RijndaelManaged байты неправильно расшифровываются из varbinary (max) - PullRequest
0 голосов
/ 27 марта 2019

Я пытаюсь зашифровать / расшифровать документы в таблицу с помощью хранимой процедуры, поэтому я создал сборку CLR с функциями шифрования / дешифрования, которые используют класс RijndaelManaged. Я могу зашифровать байты, но когда я расшифровываю байты и сохраняю документ, я замечаю, что существует разница в кодировке, которая нарушает документ. Я отправляю байты varbinary(max) непосредственно в функцию шифрования / дешифрования, поэтому я не уверен, что вызывает другую кодировку. Мне интересно, как я могу получить это для расшифровки в правильной кодировке?

Вот как выглядит моя сборка:

    public static byte[] AES_EncryptBytes(byte[] input, string pass)
    {
        try
        {
            return EncryptBytesToBytes(input, System.Text.Encoding.UTF8.GetBytes(pass));
        }
        catch (Exception)
        {
            return null;
        }
    }

    public static byte[] AES_DecryptBytes(byte[] input, string pass)
    {
        try
        {
            return DecryptBytesFromBytes(input, System.Text.Encoding.UTF8.GetBytes(pass));
        }
        catch (Exception)
        {
            return null;
        }
    }

    private static byte[] EncryptBytesToBytes(byte[] Input, byte[] Key)
    {
        return EncryptBytesToBytes(Input, Key, null);
    }

    private static byte[] EncryptBytesToBytes(byte[] Input, byte[] Key, byte[] IV)
    {
        // Check arguments.
        if ((Input == null) || (Input.Length <= 0))
        {
            throw (new ArgumentNullException("plainText"));
        }

        if ((Key == null) || (Key.Length <= 0))
        {
            throw (new ArgumentNullException("Key"));
        }

        // Create an RijndaelManaged object
        // with the specified key and IV.
        RijndaelManaged rijAlg = new RijndaelManaged();
        rijAlg.Key = Key;

        if (!(IV == null))
        {
            if (IV.Length > 0)
            {
                rijAlg.IV = IV;
            }
            else
            {
                rijAlg.Mode = CipherMode.ECB;
            }
        }
        else
        {
            rijAlg.Mode = CipherMode.ECB;
        }

        byte[] encrypted = null;
        // Create a decrytor to perform the stream transform.
        ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

        encrypted = encryptor.TransformFinalBlock(Input, 0, Input.Length);
        // Return the encrypted bytes from the memory stream.
        return encrypted;
    }

    private static byte[] DecryptBytesFromBytes(byte[] cipherText, byte[] Key)
    {
        return DecryptBytesFromBytes(cipherText, Key, null);
    }

    private static byte[] DecryptBytesFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
    {
        // Check arguments.
        if ((cipherText == null) || (cipherText.Length <= 0))
        {
            throw (new ArgumentNullException("cipherText"));
        }

        if ((Key == null) || (Key.Length <= 0))
        {
            throw (new ArgumentNullException("Key"));
        }

        // Create an RijndaelManaged object
        // with the specified key and IV.
        RijndaelManaged rijAlg = new RijndaelManaged();
        rijAlg.Key = Key;

        if (!(IV == null))
        {
            if (IV.Length > 0)
            {
                rijAlg.IV = IV;
            }
            else
            {
                rijAlg.Mode = CipherMode.ECB;
            }
        }
        else
        {
            rijAlg.Mode = CipherMode.ECB;
        }

        byte[] output = null;
        // Create a decrytor to perform the stream transform.
        ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
        // Create the streams used for decryption.
        MemoryStream msDecrypt = new MemoryStream(cipherText);
        CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

        StreamReader srDecrypt = new StreamReader(csDecrypt);
        // Read the decrypted bytes from the decrypting stream
        // and place them in a string.
        MemoryStream ms = new MemoryStream();

        while (!srDecrypt.EndOfStream)
        {
            ms.WriteByte((byte)(srDecrypt.Read()));
        }

        ms.Position = 0;
        output = ms.ToArray();
        return output;
    }

Вот как выглядят мои функции:

CREATE FUNCTION [dbo].EncryptBytes
     (@Input VARBINARY(MAX), @KEY [NVARCHAR](100))
RETURNS VARBINARY(MAX) 
WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME [DocumentsEncryption].[AES_EncryptDecrypt.AES_EncryptDecryptLibrary].AES_EncryptBytes
GO

CREATE FUNCTION [dbo].[DecryptBytes]
    (@Input VARBINARY(MAX), @KEY [NVARCHAR](100))
RETURNS VARBINARY(MAX) 
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [DocumentsEncryption].[AES_EncryptDecrypt.AES_EncryptDecryptLibrary].[AES_DecryptBytes]
GO

И, например, как это выполняется:

DECLARE @DocumentStream VARBINARY(MAX)
--these bytes below represent a document
SET @DocumentStream = 0x255044462D312E350D25E2E3CFD30D0A

DECLARE @EncryptionKey NVARCHAR(100)
SET @EncryptionKey = 'ayb&e#i&BWLGMe2V'

DECLARE @EncryptedDocumentStream VARBINARY(MAX)
SET @EncryptedDocumentStream  = dbo.[EncryptBytes](@DocumentStream, @EncryptionKey)

DECLARE @DecryptedDocumentStream VARBINARY(MAX)
SET @DecryptedDocumentStream = dbo.[DecryptBytes](@EncryptedDocumentStream,@EncryptionKey)

--@DecryptedDocumentStream will return the decrypted bytes but the encoding is wrong
SELECT @DecryptedDocumentStream
--This will return:               0x255044462D312E350D25FDFDFDFD0D0A
--Instead of the original bytes:  0x255044462D312E350D25E2E3CFD30D0A

Before and After byte comparison

1 Ответ

1 голос
/ 27 марта 2019

Проблема «где-то» во всем этом коде обработки потока в вашем методе расшифровки.Я говорю это, потому что я не собираюсь копаться и искать точную ошибку.Первое, что выскочило, это то, что ваши методы шифрования и дешифрования не выглядят «симметрично» - они выполняют примерно одинаковые действия (но некоторые операции меняются местами).Обычно это плохой знак с парами методов шифрования / дешифрования 1 .

Так что если я делаю дешифрование похожим на шифрование и не выполняю всю работу по обработке потоков:

private static byte[] DecryptBytesFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
  if ((cipherText == null) || (cipherText.Length <= 0))
  {
    throw (new ArgumentNullException("cipherText"));
  }

  if ((Key == null) || (Key.Length <= 0))
  {
    throw (new ArgumentNullException("Key"));
  }

  RijndaelManaged rijAlg = new RijndaelManaged();
  rijAlg.Key = Key;

  if (!(IV == null))
  {
    if (IV.Length > 0)
    {
      rijAlg.IV = IV;
    }
    else
    {
      rijAlg.Mode = CipherMode.ECB;
    }
  }
  else
  {
    rijAlg.Mode = CipherMode.ECB;
  }

  ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
  return decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length);
}

(я также пропустил наличие переменной output - я не видел необходимости в этом, и комментарии просто рассказывали нам , что делает код, который мы можем определить, прочитавкод).

Теперь это (в сочетании с EncryptBytesToBytes в вашем вопросе) может успешно выполнить обход данных примера:

static void Main()
{
  var inp = new byte[] { 0x25, 0x50, 0x44, 0x46, 0x2D, 0x31, 0x2E, 0x35,
                         0x0D, 0x25, 0xE2, 0xE3, 0xCF, 0xD3, 0x0D, 0x0A };
  var key = "ayb&e#i&BWLGMe2V";

  var oup = AES_DecryptBytes(AES_EncryptBytes(inp, key), key);
  Console.ReadLine();
}

На глаз, inp и oupсодержат те же данные.

(вставьте обычные предостережения о том, что ЕЦБ является ужасным режимом, если только он не выбран по очень особым причинам)


1 Моя обычная рекомендация, если вы собираетесь создать пару методов шифрования / дешифрования, это сделать медленно и просто и убедиться, что пара может округляться-подходите на каждом этапе, прежде чем добавлять больше сложности.

Первый этапЭто просто "возвращает входной буфер, игнорирует ключ и IV".Напишите несколько модульных тестов, которые подтверждают его циклические переходы с некоторыми буферами приличного размера и определенным ключом и IV.

Затем добавьте немного больше сложности к реализации и убедитесь, что модульные тесты по-прежнемупроходите и выполняйте итерацию до тех пор, пока методы не сделают то, что вы хотите / нуждаетесь в них.

Если вам нужно «зашифровать на одном языке, расшифровать на другом», я бы фактически рекомендовал делать все это дважды, воба языка, так что они оба имеют оба набора методов.Затем убедитесь, что выходы на каждом этапе соответствуют между вашими реализациями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...