Вопрос

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

Когда пользователь вводит неправильный пароль, большую часть времени CryptographicException брошен с сообщением «Надое неверно и не может быть удалено».

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

Есть идеи, как обнаружить/предотвратить это? Самым простым способом, который я могу придумать, было бы поместить «волшебный номер» в начале сообщения при шифровании, и проверить, есть ли он все еще там после расшифровки.

Но если есть более простой способ, я бы хотел услышать это!

Это было полезно?

Решение

HMAC - это то, что вам нужно. Это точно сделано для этой цели. Он объединяет ключ и сообщение (которое в данном случае будет вашим паролем) и хэшируют их таким образом, чтобы обеспечить подлинность и целостность содержания, если используемая функция хеш безопасна. Вы можете прикрепить HMAC к зашифрованным данным, и его можно использовать позже для проверки того, было ли сделано дешифрование правильно.

Другие советы

Контрольные суммы именно для этой цели. Получите хэш ваших данных перед шифрованием. Зашифруйте данные и поместите их вместе с хэшем в хранилище. После расшифровки получите хэш расшифрованных данных и сравните их с первыми. Если вы используете хэш крипто -класса (то есть SHA512), ваши данные будут безопасными. В конце концов, это именно то, что делает программное обеспечение для зашифрованного сжатия.

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

Чтобы проверить, правильно ли пароль, который вы используете, вы можете использовать этот код

            Dim decryptedByteCount As Integer
            Try
                decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
            Catch exp As System.Exception
                Return "Password Not Correct"
            End Try

По сути, проверьте, генерируется ли сообщение об ошибке во время расшифровки.

Я сообщаю обо всем код декодирования ниже

    Public Shared Function Decrypt(ByVal cipherText As String) As String

    If System.Web.HttpContext.Current.Session("Crypto") = "" Then
        HttpContext.Current.Response.Redirect("http://yoursite.com")
    Else
        If cipherText <> "" Then
            'Setto la password per criptare il testo
            Dim passPhrase As String = System.Web.HttpContext.Current.Session("Crypto")

            'Ottieni lo stream completo di byte che rappresentano: [32 byte di Salt] + [32 byte di IV] + [n byte di testo cifrato]
            Dim cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText)

            'Ottieni i Salt bytes estraendo i primi 32 byte dai byte di testo cifrato forniti
            Dim saltStringBytes = cipherTextBytesWithSaltAndIv.Take((Keysize)).ToArray

            'Ottieni i IV byte estraendo i successivi 32 byte dai byte testo cifrato forniti.
            Dim ivStringBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize)).Take((Keysize)).ToArray

            'Ottieni i byte del testo cifrato effettivo rimuovendo i primi 64 byte dal testo cifrato.
            Dim cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(((Keysize) * 2)).Take((cipherTextBytesWithSaltAndIv.Length - ((Keysize) * 2))).ToArray

            Dim password = New Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)
            Dim keyBytes = password.GetBytes((Keysize))
            Dim symmetricKey = New RijndaelManaged
            symmetricKey.BlockSize = 256
            symmetricKey.Mode = CipherMode.CBC
            symmetricKey.Padding = PaddingMode.PKCS7

            Dim decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)
            Dim memoryStream = New MemoryStream(cipherTextBytes)
            Dim cryptoStream = New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)
            Dim plainTextBytes = New Byte((cipherTextBytes.Length) - 1) {}

            Dim decryptedByteCount As Integer
            Try
                decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
            Catch exp As System.Exception
                Return "La password di Cryptazione non è corretta"
            End Try

            memoryStream.Close()
            cryptoStream.Close()
            Return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount)
        Else
            Decrypt = ""
        End If
    End If

End Function

Хотя я могу несколько согласиться с постом Teoman Soygul о CRC/Hash, есть одна очень важная вещь, которую нужно отметить. Никогда не шифруйте хэш, так как это может облегчить поиск полученного ключа. Даже без шифрования хэша вы все еще дали им простой способ проверить, успешно ли они получили правильный пароль; Однако, давайте предположим, что это уже возможно. Поскольку я знаю, какие данные вы зашифровали, будь то текст или сериализованные объекты, или что -то в этом роде, я могу написать код, чтобы распознать его.

Тем не менее, я использовал производные следующего кода для шифрования/расшифровки данных:

    static void Main()
    {
        byte[] test = Encrypt(Encoding.UTF8.GetBytes("Hello World!"), "My Product Name and/or whatever constant", "password");
        Console.WriteLine(Convert.ToBase64String(test));
        string plain = Encoding.UTF8.GetString(Decrypt(test, "My Product Name and/or whatever constant", "passwords"));
        Console.WriteLine(plain);
    }
    public static byte[] Encrypt(byte[] data, string iv, string password)
    {
        using (RijndaelManaged m = new RijndaelManaged())
        using (SHA256Managed h = new SHA256Managed())
        {
            m.KeySize = 256;
            m.BlockSize = 256;
            byte[] hash = h.ComputeHash(data);
            byte[] salt = new byte[32];
            new RNGCryptoServiceProvider().GetBytes(salt);
            m.IV = h.ComputeHash(Encoding.UTF8.GetBytes(iv));
            m.Key = new Rfc2898DeriveBytes(password, salt) { IterationCount = 10000 }.GetBytes(32);

            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write(hash, 0, hash.Length);
                ms.Write(salt, 0, salt.Length);
                using (CryptoStream cs = new CryptoStream(ms, m.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(data, 0, data.Length);
                    cs.FlushFinalBlock();
                    return ms.ToArray();
                }
            }
        }
    }

    public static byte[] Decrypt(byte[] data, string iv, string password)
    {
        using (MemoryStream ms = new MemoryStream(data, false))
        using (RijndaelManaged m = new RijndaelManaged())
        using (SHA256Managed h = new SHA256Managed())
        {
            try
            {
                m.KeySize = 256;
                m.BlockSize = 256;

                byte[] hash = new byte[32];
                ms.Read(hash, 0, 32);
                byte[] salt = new byte[32];
                ms.Read(salt, 0, 32);

                m.IV = h.ComputeHash(Encoding.UTF8.GetBytes(iv));
                m.Key = new Rfc2898DeriveBytes(password, salt) { IterationCount = 10000 }.GetBytes(32);
                using (MemoryStream result = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, m.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = cs.Read(buffer, 0, buffer.Length)) > 0)
                            result.Write(buffer, 0, len);
                    }

                    byte[] final = result.ToArray();
                    if (Convert.ToBase64String(hash) != Convert.ToBase64String(h.ComputeHash(final)))
                        throw new UnauthorizedAccessException();

                    return final;
                }
            }
            catch
            {
                //never leak the exception type...
                throw new UnauthorizedAccessException();
            }
        }
    }

Мне нравится Может ли ответ Генсера; Вы не можете действительно проверить дешифрование без HMAC.

Но если у вас очень большой открытый текст, то дешифрование может быть очень дорогим. Вы можете сделать тонну работы, чтобы узнать, что пароль был недействительным. Было бы неплохо иметь возможность сделать быстрый отказ от неправильных паролей, не проходя через всю эту работу. Есть способ использования PKCS#5 PBKDF2. Анкет (стандартизировано в RFC2898, который доступен для вашей программы C# в Rfc2898deivebytes).

Обычно протокол данных требует генерации ключа из пароля и соли с использованием PBKDF2, 1000 циклов или некоторого указанного номера. Тогда, возможно, также (необязательно) вектор инициализации посредством продолжения того же алгоритма.

Чтобы реализовать быструю проверку пароля, генерируйте Еще два байта через PBKDF2. Если вы не генерируете и используете IV, просто генерируйте 32 байта и сохраните последний 2. Храните или передайте эту пару байтов, прилегающих к вашему Cryptotext. На стороне расшифровки получите пароль, генерируйте ключ и (возможно, выброс) IV, затем сгенерируйте 2 дополнительных байта и проверьте их на сохраненные данные. Если пары вам не совпадают знать У вас неправильный пароль, без какого -либо дешифрования.

Если они соответствуют, это не гарантия того, что пароль правильный. Вам все еще нужен HMAC полного открытого текста для этого. Но вы можете сэкономить кучу работы и, возможно, время на стене, в большинстве случаев «неправильного пароля» и без ущерба для безопасности общей системы.


пса: вы написали:

Самым простым способом, который я могу придумать, было бы поместить «волшебный номер» в начале сообщения при шифровании, и проверить, есть ли он все еще там после расшифровки.

Избегайте открытого текста в Cryptotext. Он только выявляет еще один вектор атаки, облегчает злоумышленнику устранить неправильные повороты. Проверка пароля, которую я упомянул выше, - это другое животное, не подвергает этот риск.

 Public Sub decryptFile(ByVal input As String, ByVal output As String)

        inputFile = New FileStream(input, FileMode.Open, FileAccess.Read)
        outputFile = New FileStream(output, FileMode.OpenOrCreate, FileAccess.Write)
        outputFile.SetLength(0)

        Dim buffer(4096) As Byte
        Dim bytesProcessed As Long = 0
        Dim fileLength As Long = inputFile.Length
        Dim bytesInCurrentBlock As Integer
        Dim rijandael As New RijndaelManaged
        Dim cryptoStream As CryptoStream = New CryptoStream(outputFile, rijandael.CreateDecryptor(encryptionKey, encryptionIV), CryptoStreamMode.Write)

        While bytesProcessed < fileLength
            bytesInCurrentBlock = inputFile.Read(buffer, 0, 4096)
            cryptoStream.Write(buffer, 0, bytesInCurrentBlock)
            bytesProcessed = bytesProcessed + CLng(bytesInCurrentBlock)
        End While
        Try
            cryptoStream.Close() 'this will raise error if wrong password used
            inputFile.Close()
            outputFile.Close()
            File.Delete(input)
            success += 1
        Catch ex As Exception
            fail += 1
            inputFile.Close()
            outputFile.Close()
            outputFile = Nothing
            File.Delete(output)
        End Try

Я использую этот код для расшифровки любого файла. Неправильный пароль, обнаруженный на cryptostream.close(). Анкет Поймайте эту строку как ошибку, когда неправильный ключ используется для расшифровки файла. Когда происходит ошибка, просто закройте выходной поток и отпустите его (установите outputFile к Nothing), затем удалите выходной файл. Это работает для меня.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top