10

プログラム内の機密データを暗号化するためにRijndaelを使用しています。

ユーザーが間違ったパスワードを入力すると、ほとんどの場合、CryptographicException「パディングは無効であり、削除できません」というメッセージとともにaがスローされます。

ただし、非常に低い確率で、CryptStreamは間違ったパスワードで例外をスローせず、代わりに誤って復号化されたストリームを返します。言い換えれば、それはゴミに復号化します。

これを検出/防止する方法はありますか?私が考える最も簡単な方法は、暗号化するときにメッセージの先頭に「マジックナンバー」を付け、復号化した後もまだそこにあるかどうかを確認することです。

しかし、もっと簡単な方法があれば、ぜひ聞いてみたいです!

4

6 に答える 6

7

HMACはあなたが必要とするものです。それはまさにこの目的のために作られています。キーとメッセージ(この場合はパスワード)を組み合わせて、使用されるハッシュ関数が安全である限り、コンテンツの信頼性と整合性を保証する方法でそれらをハッシュします。HMACを暗号化されたデータに接続し、後で復号化が正しく行われたかどうかを検証するために使用できます。

于 2011-04-28T18:12:34.923 に答える
3

チェックサムはまさにこの目的のためのものです。暗号化する前にデータのハッシュを取得します。データを暗号化し、ハッシュと一緒にストレージに入れます。復号化した後、復号化されたデータのハッシュを取得し、前者と比較します。暗号グレードのハッシュ(つまりSHA512)を使用すると、データは安全になります。結局のところ、これはまさに暗号化された圧縮ソフトウェアが行うことです。

究極のセキュリティを実現するために、ハッシュとデータの両方を別々に暗号化してから、復号化して比較することができます。データとハッシュの両方が破損したデータに復号化する場合、それらが一致する可能性はごくわずかです。

于 2011-04-28T00:38:58.233 に答える
1

使用しているパスワードが正しいかどうかを確認するには、このコードを使用できます

            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
于 2018-07-12T10:26:22.913 に答える
0

CRC /Hashに関するTeomanSoygulの投稿にはある程度同意できますが、注意すべき非常に重要なことが1つあります。ハッシュを暗号化しないでください。暗号化すると、結果のキーを見つけやすくなります。ハッシュを暗号化せずに、正しいパスワードを正常に取得したかどうかをテストする簡単な方法を提供しました。ただし、それはすでに可能であると仮定しましょう。テキストやシリアル化されたオブジェクトなど、暗号化したデータの種類がわかっているので、それを認識するコードを記述できる可能性があります。

そうは言っても、私は次のコードの派生物を使用してデータを暗号化/復号化しました。

    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();
            }
        }
    }
于 2011-04-28T01:54:53.230 に答える
-1

CanGencerの答えが好きです; HMACなしでは復号化を実際に検証することはできません。

ただし、非常に大きな平文がある場合、復号化には非常にコストがかかる可能性があります。パスワードが無効であることを確認するためだけに、大量の作業を行う可能性があります。すべての作業を行わずに、間違ったパスワードをすばやく拒否できると便利です。PKCS#5PBKDF2を使用する方法があります。(RFC2898で標準化されており、 Rfc2898DeriveBytesのc#プログラムにアクセスできます)。

通常、データプロトコルは、1000サイクルまたは指定された数で、PBKDF2を使用してパスワードとソルトからキーを生成することを要求します。次に、同じアルゴリズムの連続を介して、(オプションで)初期化ベクトルも使用できます。

クイックパスワードチェックを実装するには、PBKDF2を介してさらに2バイトを生成します。IVを生成して使用しない場合は、32バイトを生成し、最後の2バイトを保持します。このバイトのペアを暗号化テキストに隣接して保存または送信します。復号化側では、パスワードを取得し、キーと(おそらく使い捨ての)IVを生成してから、さらに2バイトを生成し、保存されているデータと照合します。ペアが一致しない場合は、復号化せずにパスワードが間違っていることがわかります。

それらが一致する場合、パスワードが正しいことを保証するものではありません。そのためには、まだ完全な平文のHMACが必要です。しかし、ほとんどの場合「パスワードが間違っている」場合は、システム全体のセキュリティを損なうことなく、大量の作業を節約でき、場合によっては実時間を節約できます。


ps:あなたが書いた:

私が考える最も簡単な方法は、暗号化するときにメッセージの先頭に「マジックナンバー」を付け、復号化した後もまだそこにあるかどうかを確認することです。

平文を暗号文に入れることは避けてください。それは別の攻撃ベクトルを公開するだけであり、攻撃者が間違ったターンを排除するのを容易にします。私が上で述べたパスワード検証は別の動物であり、このリスクを露呈しません。

于 2011-05-23T20:45:16.630 に答える
-1
 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()。間違ったキーがファイルの復号化に使用された場合、この行をエラーとしてキャッチします。エラーが発生した場合は、出力ストリームを閉じて放し(に設定outputFileNothing、出力ファイルを削除します。それは私のために働いています。

于 2017-08-25T13:37:36.760 に答える