ASP.NET AES Padding недействителен и не может быть удален

Это обычная проблема, но решения, предложенные здесь и здесь, не работают для меня, также они не включают базу данных.

Я хотел бы, чтобы пример кода "Сбой кода (с базой данных)" работал.

Это мой код шифрования / дешифрования (скопирован здесь: https://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx?cs-save-lang=1&cs-lang=vb&f=255&MSPPError=-2147217396):

Shared Function EncryptStringToBytes_Aes(ByVal plainText As String, ByVal Key() As Byte, ByVal IV() As Byte) As Byte()
    ' Check arguments.
    If plainText Is Nothing OrElse plainText.Length <= 0 Then
        Throw New ArgumentNullException("plainText")
    End If
    If Key Is Nothing OrElse Key.Length <= 0 Then
        Throw New ArgumentNullException("Key")
    End If
    If IV Is Nothing OrElse IV.Length <= 0 Then
        Throw New ArgumentNullException("Key")
    End If
    Dim encrypted() As Byte
    ' Create an Aes object
    ' with the specified key and IV.
    Using aesAlg As Aes = Aes.Create()

        aesAlg.Key = Key
        aesAlg.IV = IV

        ' Create a decrytor to perform the stream transform.
        Dim encryptor As ICryptoTransform = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV)
        ' Create the streams used for encryption.
        Using msEncrypt As New MemoryStream()
            Using csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
                Using swEncrypt As New StreamWriter(csEncrypt)

                    'Write all data to the stream.
                    swEncrypt.Write(plainText)
                End Using
                encrypted = msEncrypt.ToArray()
            End Using
        End Using
    End Using

    ' Return the encrypted bytes from the memory stream.
    Return encrypted

End Function 'EncryptStringToBytes_Aes


Shared Function DecryptStringFromBytes_Aes(ByVal cipherText() As Byte, ByVal Key() As Byte, ByVal IV() As Byte) As String
    ' Check arguments.
    If cipherText Is Nothing OrElse cipherText.Length <= 0 Then
        Throw New ArgumentNullException("cipherText")
    End If
    If Key Is Nothing OrElse Key.Length <= 0 Then
        Throw New ArgumentNullException("Key")
    End If
    If IV Is Nothing OrElse IV.Length <= 0 Then
        Throw New ArgumentNullException("Key")
    End If
    ' Declare the string used to hold
    ' the decrypted text.
    Dim plaintext As String = Nothing

    ' Create an Aes object
    ' with the specified key and IV.
    Using aesAlg As Aes = Aes.Create()
        aesAlg.Key = Key
        aesAlg.IV = IV

        ' Create a decrytor to perform the stream transform.
        Dim decryptor As ICryptoTransform = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV)

        ' Create the streams used for decryption.
        Using msDecrypt As New MemoryStream(cipherText)

            Using csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)

                Using srDecrypt As New StreamReader(csDecrypt)
                    ' Read the decrypted bytes from the decrypting stream
                    ' and place them in a string.
                    plaintext = srDecrypt.ReadToEnd()
                End Using   <= PADDING ERROR THROWN HERE
            End Using

            'cipherText = msDecrypt.ToArray() 'added by me

        End Using
    End Using

    Return plaintext

End Function 'DecryptStringFromBytes_Aes 

Рабочий образец (без базы данных)

    Dim original As String = "Here is some data to encrypt!"

    Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)

    Try

        ' Create a new instance of the Aes
        ' class.  This generates a new key and initialization 
        ' vector (IV).
        Using myAes As Aes = Aes.Create()

            myAes.Key = key.GetBytes(myAes.KeySize / 8)
            myAes.IV = key.GetBytes(myAes.BlockSize / 8)

            ' Encrypt the string to an array of bytes.
            Dim encrypted As Byte() = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV)

            ' Decrypt the bytes to a string.
            Dim roundtrip As String = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV)

            'Display the original data and the decrypted data.
            ltStatus.Text = String.Format("Original:   {0}", original)
            ltStatus.Text += String.Format("Round Trip: {0}", roundtrip)
        End Using
    Catch ex As Exception
        Console.WriteLine("Error: {0}", ex.Message)
    End Try

Сбой кода (с базой данных)

    Dim _salt As Byte() = Encoding.ASCII.GetBytes("o6806642kbM7c5")
    Dim _sharedSecret As String = "abcd"
    Dim original As String = "Here is some data to encrypt!"
    Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
    Dim encrypted As Byte()
    Try
        Using myAes As Aes = Aes.Create()
            myAes.Key = key.GetBytes(myAes.KeySize / 8)
            myAes.IV = key.GetBytes(myAes.BlockSize / 8)
            myAes.Padding = PaddingMode.PKCS7
            encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV)
        End Using
    Catch ex As Exception
        Console.WriteLine("Error: {0}", ex.Message)
    End Try
    'save to DB
    Dim myConnection As SqlConnection = GetConnection()
    Dim cmd As New SqlCommand("UPDATE banks set bank_name=@bankname WHERE id=1", myConnection)
    cmd.Parameters.Add(New SqlParameter("@bankname", encrypted))
    Try
        myConnection.Open()
        cmd.ExecuteScalar()
    Catch ex As Exception
        GlobalFunctions.LogError("banks:INSERT encrypted", ex.Message, LogLevel.Normal)
    Finally
        myConnection.Close()
    End Try

    'retreive from db
    Dim decrypted As String = ""
    myConnection = GetConnection()
    cmd = New SqlCommand("SELECT bank_name FROM banks where id=1", myConnection)
    Dim reader As SqlDataReader
    Try
        myConnection.Open()
        reader = cmd.ExecuteReader
        If reader.Read Then
            Using myAes As Aes = Aes.Create()
                myAes.Key = key.GetBytes(myAes.KeySize / 8)
                myAes.IV = key.GetBytes(myAes.BlockSize / 8)
                myAes.Padding = PaddingMode.PKCS7
                decrypted = DecryptStringFromBytes_Aes(reader("bank_name"), myAes.Key, myAes.IV)
            End Using
        Else
            GlobalFunctions.LogError("banks:nothing to be read?!?", LogLevel.Normal)
        End If

    Catch ex As Exception
        GlobalFunctions.LogError("banks:SELECT encrypted.", ex.Message, LogLevel.Normal)
    Finally
        myConnection.Close()
    End Try

Но здесь что-то идет не так:

Двоичное значение успешно добавлено в мою базу данных MSSQL в поле bank_name типа varbinary(MAX), (Я также пробовал меньшие поля, например, varbinary(50))

Но когда я пытаюсь расшифровать это поле после восстановления из базы данных, я получаю сообщение об ошибке Padding is invalid and cannot be removed. См. Кодовую строку с комментарием "<= ОШИБКА ПАДИРОВАНИЯ ЗДЕСЬ" в приведенном выше коде "Сбой кода (с базой данных)".

Я проверил здесь и здесь. И я не пропускаю пустую строку. Также я попытался добавить cipherText = msDecrypt.ToArray(), но ошибка уже возникает до попадания в эту строку.

ОБНОВЛЕНИЕ 2

Мои сброшенные значения:

ReportError хранит значения в текстовом поле в БД, сообщаемые значения:

myAes.Key в
00000000 95 0C 95 EA 1D 40 0C FB 1D 3F B7 FB 73 FB 3F EA 00000010 40 62 51 62 51 EA 62 73 B7 2E 1D C8 1D 51 51 95

myAes.IV в
00000000 51 A6 84 73 95 C8 2E 62 84 C8 0C 62 C8 2E 1D 84

зашифрован в
00000000 FB FB B7 73 D9 51 A6 2E 95 73 62 73 3F 84 A6 40 00000010 B7 62 84 2E 51 95 EA 1D 51 A6 EA 2E 51 A6 51 95

myAes.Key Out
00000000 51 1D 73 40 EA A6 73 EA FB 73 73 A6 0C A6 D9 1D 00000010 2E 3F FB 2E 73 A6 A6 0C A6 C8 95 0C D9 1D B7 73

myAes.IV out
00000000 B7 95 51 73 B7 D9 95 EA 0C C8 95 95 0C 84 40 62

зашифрованы
00000000 FB FB B7 73 D9 51 A6 2E 95 73 62 73 3F 84 A6 40 00000010 B7 62 84 2E 51 95 EA 1D 51 A6 EA 2E 51 A6 51 95

банки: ВЫБЕРИТЕ зашифровано. Заполнение недействительно и не может быть удалено.

Вот полный код, который я сейчас использую:

Imports System.Security.Cryptography
Imports System.Data.SqlClient
Imports System.Text

Namespace HexDump
    Class Utils
        Public Shared Function HexDump(bytes As Byte(), Optional bytesPerLine As Integer = 16) As String
            If bytes Is Nothing Then
                Return "<null>"
            End If
            Dim bytesLength As Integer = bytes.Length

            Dim HexChars As Char() = "0123456789ABCDEF".ToCharArray()

            ' 8 characters for the address
            Dim firstHexColumn As Integer = 8 + 3
            ' 3 spaces
            ' - 2 digit for the hexadecimal value and 1 space
            ' - 1 extra space every 8 characters from the 9th
            Dim firstCharColumn As Integer = firstHexColumn + bytesPerLine * 3 + (bytesPerLine - 1) / 8 + 2
            ' 2 spaces 
            ' - characters to show the ascii value
            Dim lineLength As Integer = firstCharColumn + bytesPerLine + Environment.NewLine.Length
            ' Carriage return and line feed (should normally be 2)
            Dim line As Char() = (New [String](" "c, lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray()
            Dim expectedLines As Integer = (bytesLength + bytesPerLine - 1) / bytesPerLine
            Dim result As New StringBuilder(expectedLines * lineLength)

            Dim i As Integer = 0
            While i < bytesLength
                line(0) = HexChars((i >> 28) And &HF)
                line(1) = HexChars((i >> 24) And &HF)
                line(2) = HexChars((i >> 20) And &HF)
                line(3) = HexChars((i >> 16) And &HF)
                line(4) = HexChars((i >> 12) And &HF)
                line(5) = HexChars((i >> 8) And &HF)
                line(6) = HexChars((i >> 4) And &HF)
                line(7) = HexChars((i >> 0) And &HF)

                Dim hexColumn As Integer = firstHexColumn
                Dim charColumn As Integer = firstCharColumn

                For j As Integer = 0 To bytesPerLine - 1
                    If j > 0 AndAlso (j And 7) = 0 Then
                        hexColumn += 1
                    End If
                    If i + j >= bytesLength Then
                        line(hexColumn) = " "c
                        line(hexColumn + 1) = " "c
                        line(charColumn) = " "c
                    Else
                        'Dim b As Byte = bytes(i + j)
                        'line(hexColumn) = HexChars((b >> 4) And &HF)
                        'line(hexColumn + 1) = HexChars(b And &HF)
                        'line(charColumn) = (If(b < 32, "·"c, CChar(b)))
                        Dim b As Byte = bytes((i + j))
                        line(hexColumn) = HexChars(((b + 4) _
             And 15))
                        line((hexColumn + 1)) = HexChars((b And 15))
                        line(charColumn) = Microsoft.VisualBasic.ChrW(65533)

                    End If
                    hexColumn += 3
                    charColumn += 1
                Next
                result.Append(line)
                i += bytesPerLine
            End While
            Return result.ToString()
        End Function
    End Class
End Namespace




Public Class banks_financial
    Inherits System.Web.UI.Page

    Private _lang As String
    Private _registryId As Integer

    Private _salt As Byte() = Encoding.ASCII.GetBytes("o6806642kbM7c5")
    Private _sharedSecret As String = "abcd"

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim original As String = "Here is some data to encrypt!"
        Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
        Dim encrypted As Byte()
        Try
            Using myAes As Aes = Aes.Create()
                myAes.Key = key.GetBytes(myAes.KeySize / 8)
                myAes.IV = key.GetBytes(myAes.BlockSize / 8)
                myAes.Padding = PaddingMode.PKCS7
                encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV)

                ReportError("myAes.Key in", HexDump.Utils.HexDump(myAes.Key))
                ReportError("myAes.IV in", HexDump.Utils.HexDump(myAes.IV))
                ReportError("encrypted in", HexDump.Utils.HexDump(encrypted))

            End Using
        Catch ex As Exception
            Console.WriteLine("Error: {0}", ex.Message)
        End Try
        'save to DB
        Dim myConnection As SqlConnection = GetConnection()
        Dim cmd As New SqlCommand("UPDATE banks set bank_name=@bankname WHERE id=1", myConnection)
        cmd.Parameters.Add(New SqlParameter("@bankname", encrypted))
        Try
            myConnection.Open()
            cmd.ExecuteScalar()
        Catch ex As Exception
            GlobalFunctions.ReportError("banks:INSERT encrypted", ex.Message, LogLevel.Normal)
        Finally
            myConnection.Close()
        End Try

        'retreive from db
        Dim decrypted As String = ""
        myConnection = GetConnection()
        cmd = New SqlCommand("SELECT bank_name FROM banks where id=1", myConnection)
        Dim reader As SqlDataReader
        Try
            myConnection.Open()
            reader = cmd.ExecuteReader
            If reader.Read Then
                Using myAes As Aes = Aes.Create()
                    myAes.Key = key.GetBytes(myAes.KeySize / 8)
                    myAes.IV = key.GetBytes(myAes.BlockSize / 8)
                    myAes.Padding = PaddingMode.PKCS7

                    ReportError("myAes.Key out", HexDump.Utils.HexDump(myAes.Key))
                    ReportError("myAes.IV out", HexDump.Utils.HexDump(myAes.IV))
                    ReportError("encrypted out", HexDump.Utils.HexDump(reader("bank_name")))
                    decrypted = DecryptStringFromBytes_Aes(reader("bank_name"), myAes.Key, myAes.IV)
                    ReportError("decrypted", decrypted)
                End Using
            Else
                GlobalFunctions.ReportError("banks:nothing to be read?!?", LogLevel.Normal)
            End If

        Catch ex As Exception
            GlobalFunctions.ReportError("banks:SELECT encrypted.", ex.Message, LogLevel.Normal)
        Finally
            myConnection.Close()
        End Try

        ltStatus.Text = GetMessageStatus(decrypted, MsgType.ok)





End Class

2 ответа

Решение

Непосредственная проблема заключается в том, что ваш тестовый код используется неправильно Rfc2898DeriveBytes/ PBKDF. Это не имеет ничего общего с базой данных. Каждый раз, когда вы звоните GetBytes() в данном случае он возвращает другой набор байтов (по замыслу!). Это видно по небольшому коду:

    Dim salt = CryptoTools.GetRandomBytes(16)
    Dim PBKDF = New Rfc2898DeriveBytes("secret", salt, 10000)

    Dim a = PBKDF.GetBytes(32)
    Dim b = PBKDF.GetBytes(32)
    Dim c = PBKDF.GetBytes(32)

    If a.SequenceEqual(b) = False Then
        Console.WriteLine("A != B")
    End If
    ...

Это очень ясно с Intellisense:

введите описание изображения здесь

Массивы совсем не похожи. Из раздела "Замечания" в MSDN для GetBytes:

Класс Rfc2898DeriveBytes принимает пароль, солт и число итераций, а затем генерирует ключи посредством вызовов метода GetBytes. Повторные вызовы этого метода не будут генерировать один и тот же ключ; ...

Акцент мой, но он призван подчеркнуть, что это генератор ключей, а не просто хеш-код. Итак, в вашем коде "Fails with Database" у вас есть этот блок:

Dim key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
...
Using myAes As Aes = Aes.Create()
    ...
    myAes.Key = key.GetBytes(myAes.KeySize / 8)
    myAes.IV = key.GetBytes(myAes.BlockSize / 8)
    ...
End Using

Ключ и сгенерированный IV не будут одинаковыми. Это хорошо, но, вероятно, не то, что вы хотели. Этот же блок используется позже для расшифровки того, что было прочитано обратно из БД. Ключ и IV, используемые для расшифровки, также будут отличаться друг от друга, но, что более важно, они не будут теми же ключом и IV, которые использовались для шифрования!

Это фатальный недостаток, и вызывает ошибку. Это выглядит как вводящее в заблуждение сообщение об ошибке, но его просто воспроизвести / исправить. Вам нужен новый PBKDF, чтобы "начать заново" в части расшифровки, или Reset существующий:

' after this line...
Dim decrypted As String = ""
' ...add this:
key As Rfc2898DeriveBytes = New Rfc2898DeriveBytes(_sharedSecret, _salt)
' or
'key.Reset()

Более вероятно, что реальный код не будет выполнять обход в том же методе и использовать новый Rfc2898DeriveBytesбудет создан.

Наконец, обратите внимание, что IV и Salt должны быть уникальными и случайными для эффективного шифрования. На самом деле нет смысла шифровать каждую строку одним и тем же паролем, ключом, солью и IV за исключением иллюзии безопасности. Кто-то должен только взломать PW для одной строки, чтобы получить данные для всех строк.

Так как Rfc2898DeriveBytes является детерминированным, он может играть роль в создании IV и Salt (для данных, которые должны быть зашифрованы) без необходимости сохранять их или использовать статические.

Обычно ошибка заполнения в действительности означает, что ключ, зашифрованные данные или параметры неверны, а не заполнение как таковое.

Убедитесь, что ключ имеет правильную длину, в противном случае реализация будет использовать неопределенные байты. Сделайте ключ точно правильного размера ключа: 128, 192 или 256 бит (16, 24 или 32 байта).

Убедитесь, что ключ, данные и IV не имеют неправильной кодировки, шифрование основано на 8-битных байтах без кодирования. Некоторые библиотеки будут принимать и создавать другие кодировки, такие как Base64 и шестнадцатеричные, внимательно изучите документацию.

Шестнадцатеричный дамп ключа, IV, зашифрованных данных и дешифрованных данных и добавить их образцы к вопросу.

Вызовы функции шифрования на самом деле просты, если вы введете правильные (и полные) входы, вывод будет правильным. Если результаты проверяют входные данные тщательно.

ОБНОВИТЬ:
IV и ключ не одинаковы как для шифрования, так и для дешифрования, они должны быть одинаковыми.

Один из способов убедиться в том, что IV такой же, состоит в том, чтобы предварительно зашифровать его в зашифрованных данных при шифровании и при расшифровке восстановить его из зашифрованных данных и пропустить при расшифровке зашифрованных данных.

Ключ должен быть предварительно разделен между кодом шифрования и дешифрования каким-либо образом до дешифрования.

Другие вопросы по тегам