Warum führt ein falsches Passwort dazu, dass „Padding ungültig ist und nicht entfernt werden kann“?

StackOverflow https://stackoverflow.com/questions/11762

  •  08-06-2019
  •  | 
  •  

Frage

Ich brauchte eine einfache String-Verschlüsselung, also habe ich den folgenden Code geschrieben (mit viel „Inspiration“ von Hier):

    // create and initialize a crypto algorithm
    private static SymmetricAlgorithm getAlgorithm(string password) {
        SymmetricAlgorithm algorithm = Rijndael.Create();
        Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
            password, new byte[] {
            0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,             // salty goodness
            0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65
        }
        );
        algorithm.Padding = PaddingMode.ISO10126;
        algorithm.Key = rdb.GetBytes(32);
        algorithm.IV = rdb.GetBytes(16);
        return algorithm;
    }

    /* 
     * encryptString
     * provides simple encryption of a string, with a given password
     */
    public static string encryptString(string clearText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(clearBytes, 0, clearBytes.Length);
        cs.Close();
        return Convert.ToBase64String(ms.ToArray());
    }

    /*
     * decryptString
     * provides simple decryption of a string, with a given password
     */
    public static string decryptString(string cipherText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write);
        cs.Write(cipherBytes, 0, cipherBytes.Length);
        cs.Close();            
        return System.Text.Encoding.Unicode.GetString(ms.ToArray());
    }

Der Code scheint einwandfrei zu funktionieren, außer dass ich beim Entschlüsseln von Daten mit einem falschen Schlüssel eine CryptographicException – „Padding ist ungültig und kann nicht entfernt werden“ – in der Zeile cs.Close() in decryptString erhalte.

Beispielcode:

    string password1 = "password";
    string password2 = "letmein";
    string startClearText = "The quick brown fox jumps over the lazy dog";
    string cipherText = encryptString(startClearText, password1);
    string endClearText = decryptString(cipherText, password2);     // exception thrown

Meine Frage ist, ist das zu erwarten?Ich hätte gedacht, dass das Entschlüsseln mit dem falschen Passwort nur zu einer unsinnigen Ausgabe und nicht zu einer Ausnahme führen würde.

War es hilfreich?

Lösung

Obwohl dies bereits beantwortet wurde, denke ich, dass es eine gute Idee wäre, es zu erklären Warum es ist zu erwarten.

Ein Auffüllschema wird normalerweise angewendet, da die meisten kryptografischen Filter nicht semantisch sicher sind und einige Formen von Kryptoangriffen verhindert werden sollen.Zum Beispiel normalerweise in RSA OAEP Es wird ein Auffüllschema verwendet, das einige Arten von Angriffen verhindert (z. B. einen ausgewählten Klartextangriff oder Blendung).

Ein Auffüllschema fügt (normalerweise) zufälligen Müll an die Nachricht m an, bevor die Nachricht gesendet wird.Bei der OAEP-Methode werden beispielsweise zwei Oracles verwendet (dies ist eine vereinfachte Erklärung):

  1. Angesichts der Größe des Moduls füllen Sie k1 Bits mit 0 und k0 Bits mit einer Zufallszahl auf.
  2. Durch Anwenden einer Transformation auf die Nachricht erhalten Sie dann die aufgefüllte Nachricht, die verschlüsselt und gesendet wird.

Dadurch erhalten Sie eine Zufallsauswahl für die Nachrichten und können testen, ob es sich bei der Nachricht um Müll handelt oder nicht.Da das Padding-Schema umkehrbar ist, können Sie beim Entschlüsseln der Nachricht zwar nichts über die Integrität der Nachricht selbst sagen, aber tatsächlich eine Aussage über das Padding machen und so feststellen, ob die Nachricht korrekt entschlüsselt wurde oder Sie machen etwas falsch (z. B. jemand hat die Nachricht manipuliert oder Sie verwenden den falschen Schlüssel)

Andere Tipps

Ich habe eine ähnliche "Polsterung ist ungültig und kann nicht entfernt werden". Ausnahme, aber in meinem Fall waren die Schlüssel IV und die Polsterung korrekt.

Es stellte sich heraus, dass nur das Spülen des Krypto-Streams fehlte.

So was:

            MemoryStream msr3 = new MemoryStream();
            CryptoStream encStream = new CryptoStream(msr3, RijndaelAlg.CreateEncryptor(), CryptoStreamMode.Write);
            encStream.Write(bar2, 0, bar2.Length);
            // unless we flush the stream we would get "Padding is invalid and cannot be removed." exception when decoding
            encStream.FlushFinalBlock();
            byte[] bar3 = msr3.ToArray();

Wenn Sie möchten, dass Ihre Verwendung korrekt ist, sollten Sie hinzufügen Authentifizierung zu Ihrem Chiffretext hinzufügen, damit Sie überprüfen können, ob es sich um das richtige Passwort handelt oder ob der Chiffretext nicht geändert wurde.Die Polsterung, die Sie verwenden ISO10126 löst nur dann eine Ausnahme aus, wenn das letzte Byte nicht als einer von 16 gültigen Werten zum Auffüllen (0x01-0x10) entschlüsselt wird.Sie haben also eine Chance von 1/16, dass die Ausnahme NICHT mit dem falschen Passwort ausgelöst wird. Wenn Sie es authentifizieren, haben Sie eine deterministische Möglichkeit, festzustellen, ob Ihre Entschlüsselung gültig ist.

Die Verwendung von Krypto-APIs scheint zwar einfach zu sein, in Wirklichkeit kann es jedoch leicht zu Fehlern kommen.Beispielsweise verwenden Sie ein festes Salt für die Ableitung Ihres Schlüssels und Ihrer IV. Das bedeutet, dass jeder mit demselben Passwort verschlüsselte Chiffretext seine IV mit diesem Schlüssel wiederverwendet, wodurch die semantische Sicherheit im CBC-Modus beeinträchtigt wird. Die IV muss sowohl unvorhersehbar als auch eindeutig sein ein gegebener Schlüssel.

Damit es leicht zu Fehlern kommt, habe ich einen Codeausschnitt, den ich zu überprüfen und auf dem neuesten Stand zu halten versuche (Kommentare, Probleme willkommen):

Moderne Beispiele für symmetrische authentifizierte Verschlüsselung einer Zeichenfolge C#.

Wenn Sie es verwenden AESThenHMAC.AesSimpleDecryptWithPassword(ciphertext, password) wenn das falsche Passwort verwendet wird, null wird zurückgegeben, wenn der Chiffretext oder iv nach der Verschlüsselung geändert wurde null zurückgegeben wird, erhalten Sie niemals Junk-Daten oder eine Padding-Ausnahme zurück.

Wenn Sie eine Schlüsselinkongruenz ausgeschlossen haben, dann außerdem FlushFinalBlock() (siehe Yanivs Antwort), Anruf Close() auf der CryptoStream wird auch genügen.

Wenn Sie Ressourcen strikt mit bereinigen using Stellen Sie sicher, dass Sie den Block für die Blöcke verschachteln CryptoStream selbst:

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
{
  using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
  {
    encStream.Write(bar2, 0, bar2.Length);
  } // implicit close
  byte[] encArray = ms.ToArray();
}

Mich hat folgendes (oder ähnliches) gebissen:

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
{
  encStream.Write(bar2, 0, bar2.Length);
  byte[] encArray = ms.ToArray();
} // implicit close -- too late!

Ja, das ist zu erwarten, oder zumindest passiert genau das, wenn unsere Kryptoroutinen nicht entschlüsselbare Daten erhalten

Ein weiterer Grund für die Ausnahme könnte eine Race-Bedingung zwischen mehreren Threads sein, die Entschlüsselungslogik verwenden – native Implementierungen von ICryptoTransform sind dies nicht threadsicher (z.B.SymmetricAlgorithm), daher sollte es in den exklusiven Abschnitt eingefügt werden, z. B.verwenden sperren.Weitere Einzelheiten finden Sie hier: http://www.make-awesome.com/2011/07/system-security-cryptography-and-thread-safety/

Möglicherweise sind im CryptoStream einige ungelesene Bytes vorhanden.Das Schließen, bevor der Stream vollständig gelesen wurde, verursachte den Fehler in meinem Programm.

Ich hatte ein ähnliches Problem. Das Problem bei der Entschlüsselungsmethode bestand darin, einen leeren Speicherstream zu initialisieren.als es funktionierte, als ich es mit dem Cipher-Text-Byte-Array wie folgt initialisierte:

MemoryStream ms = new MemoryStream(cipherText)

Die vom Benutzer „atconway“ aktualisierte Antwort hat bei mir funktioniert.

Das Problem lag nicht an der Auffüllung, sondern am Schlüssel, der bei der Ver- und Entschlüsselung unterschiedlich war.Der Schlüssel und die IV sollten beim Verschlüsseln und Entschlüsseln desselben Werts gleich sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top