C # AES Rijndael - обнаружение неверных паролей - PullRequest
9 голосов
/ 28 апреля 2011

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

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

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

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

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

Ответы [ 6 ]

6 голосов
/ 28 апреля 2011

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

3 голосов
/ 28 апреля 2011

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

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

1 голос
/ 12 июля 2018

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

            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
0 голосов
/ 25 августа 2017
 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), затем удалите выходной файл. Это работает для меня.

0 голосов
/ 24 мая 2011

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

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

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

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

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


ps : Вы писали:

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

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

0 голосов
/ 28 апреля 2011

Хотя я могу несколько согласиться с постом Теомана Сойгула о 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();
            }
        }
    }
...