{the magic lies between the brackets}

Schlagwort: Rijndael (Seite 1 von 1)

[C#/.NET] AES-Ver- und Entschlüsselung mit Rijndael

Bei AES (Advanced Encryption Standard) bzw Rijndael (ausgesprochen: Rain-Dahl [rɛindaːl]) handelt es sich um einen Verschlüsselungsalgorithmus, welcher im Oktober 2000 als Standard festgelegt wurde und den Vorgänger DES (Data Encryption Standard) ablöst.
AES setzt auf eine Blockverschlüsselung, d.h. der Schlüssel wird in mehrer Rundenschlüssel eingeteilt, mit welchem pro Runde in jedem Block die Bytes ersetzt (SubBytes), verschoben (ShiftRows) und vermischt (MixColumns) werden. Es gibt je nach Schlüssellänge 10 (128 bit), 12 (192 bit) oder 14 (256 bit) Runden (R) und eine zusätzliche Endrunde, so werden R+1 Rundenschlüssel benötigt.

Für weitere Informationen empfehle ich:

Nun zur Verschlüsselung eines Strings mit AES in C# zum einen nach AES-Spezifikationen (FIPS-197), zum anderen nach Rijndael (welcher ja zum AES Algorithmus gewählt wurde).
Zwischen AES und Rijndael gibt es, trotz der Wahl von Rijndael zum Algorithmus, Unterschiede, welche eine Kompatibilität untereinander unmöglich machen.

  1. Rijndael erlaubt variable Block- und Schlüsselgrößen (128, 160, 192, 224,  256), wohingegen bei AES eine Blockgröße von 128 bit und eine Schlüsselgröße von 128, 192 oder 256 bit vorgeschrieben sind.
  2. Rijndael erlaubt zusätzlich zu Cipher Block Chaining Mode (CBC) die Verwendung von Cipher Feedback Mode (CFB) als Betriebsmodus, wohingegen AES nur Ersteres unterstützt (bzw. CFB nur mit 128 bit Feedback)

Nun aber zum Code:

[tabs active=“1″]
[tab title=“AES (FIPS-197)“]
Ich habe folgenden statische Klasse erstellt, mit der wir sämtliche Vorgänge (also Ver- und Entschlüsseln) durchführen können.
[spoiler title=“Statische AES Klasse“ style=“fancy“]
Benötigt werden folgende Namespaces:

using System; //Für sämtliche Grundfunktionen
using System.IO; //Für Streams
using System.Security.Cryptography; //Für AES
using System.Windows.Forms; //Zum Anzeigen der Fehlermeldungen (MessageBox)

 

static class AesExample
    {
        public static byte[] EncryptStringToBytes(string plainText, byte[] Key,byte[] IV)
        {
            // Überprüfe, ob alle Parameter gefüllt sind
            if ((String.IsNullOrEmpty(plainText) || (Key.Length <= 0 || Key == null) || (IV.Length <= 0 || IV == null)))
                throw new Exception("Keiner der übergebenen Werte darf leer sein");

            byte[] encrypted;

            // Erstelle ein Objekt der Klasse Aes und
            // belege den Schlüssel, sowie den Salt (Initalisierungsvektor) mit den Parametern
            using (Aes aesAlgo = Aes.Create())
            {
                aesAlgo.Key = Key;
                aesAlgo.IV = IV;

                //Verschlüsseler zur Umwandlung erstellen
                ICryptoTransform encryptor = aesAlgo.CreateEncryptor(aesAlgo.Key, aesAlgo.IV);

                // Erstelle Streams, welche für die Verschlüsselung genutzt werden
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Schreibe die Daten in den Stream
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }
            // Gebe die verschlüsselten Bytes aus dem Stream zurück
            return encrypted;
        }

        public static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            //Überprüfe, ob alle Parameter gefüllt sind
            if (((cipherText.Length <= 0 || cipherText == null) || (Key.Length <= 0 || Key == null) || (IV.Length <= 0 || IV == null)))
                throw new Exception("Keiner der übergebenen Werte darf leer sein");

            //Erstelle eine Variable, in welcher später der entschlüsselte Text gespeichert wird
            string plaintext = null;

            try
            {
                // Erstelle ein Objekt von AES mit begrenzter Gültigkeit und
                // weiße dem Schlüssel (Key) und Salt (IV), die als Parameter übergebenen Werte zu
                using (Aes aesAlgo = Aes.Create())
                {
                    aesAlgo.Key = Key;
                    aesAlgo.IV = IV;

                    // Erstelle einen Entschlüsseler, welcher den Schlüssel und Salt nutzt,
                    // um den ByteArray-Stream zu verändern (entschlüsseln)
                    ICryptoTransform decryptor = aesAlgo.CreateDecryptor(aesAlgo.Key, aesAlgo.IV);

                    // Erstelle Stream, welche zu Entschlüsselung genutzt werden
                    // Ein Stream ist eine Darstellung einer geordneten Abfolge von Bytes
                    using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                    {
                        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                            {
                                // Lese die entschlüsselten Bytes aus dem entschlüsselten Stream
                                // und füge sie komplett in die Variable, welche den entschlüsselte Text speichert.
                                plaintext = srDecrypt.ReadToEnd();
                            }
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + "\r\nMöglichweise passt der aktuelle Schlüssel oder Salt nicht mit jenem überein, welcher die Daten verschlüsselt hat");
                return null;
            }
            return plaintext;
        }

        ///
/// Wandle den String in ein ByteArray mit Base64
        /// Base64 ist ein Verfahren zur Kodierung von Binärdaten
        ///
        public static byte[] AESStringToByteArray(string cipher)
        {
            byte[] encByteArray = Convert.FromBase64String(cipher);
            return encByteArray;
        }

        ///
/// Wandelt das übergebene Bytearray in einen menschenlesbaren String.
        /// Base64 ist ein Verfahren um Binärdaten zu kodieren
        ///
        ///Die Binärdaten
        /// Den gewandelten String
        public static string AESByteArrayToString(byte[] arr)
        {
            string base64 = Convert.ToBase64String(arr);
            return base64;
        }
    }

[/spoiler]

Und so nutzt man die oben erstellte Klasse, in vollem Umfang:
[spoiler title=“Aufruf/Nutzung der AES-Klasse“ style=“fancy“]
Zu allererst erstellen wir ein Objekt, welches uns beim Ausführen des Algorithmus unterstützt. In diesem werden wir unseren Schlüssel, sowie den Salt zwischenspeichern.

Aes aesAlgo = Aes.Create();

Zusätzlich benötigen wir folgende Methode zum Aufruf der Klasse, welche den Schlüssel erstellt und zurückgibt.

        private void AES_CreateOwnSaltedKey()
        {
            CreateOwnSaltedKey eigenerSchluessel = new CreateOwnSaltedKey();
            if (eigenerSchluessel.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                aesAlgo.Key = eigenerSchluessel.Key;
                aesAlgo.IV = eigenerSchluessel.SALT;
            }
        }

Da wir nun einen Schlüssel und den Salt haben, können wir nun den String verschlüsseln.
Dies geschieht über folgende Methode:

        ///
/// Verschlüsselt den eingegebenen String mit dem gewählten Passwort und Salt.
        /// Der Salt wird in die Textbox geschrieben und ist damit öffentlich zugänglich
        ///
        private void AES_EncryptString()
        {
            byte[] encryptedByteArray = AesExample.EncryptStringToBytes(AES_orgString_textBox.Text, aesAlgo.Key, aesAlgo.IV); //Verschlüsselt den eingegeben String (siehe Klasse)
            AES_salt_textBox.Text = AesExample.AESByteArrayToString(aesAlgo.IV); //Der verschlüsseSalt wird in der Regel vor oder nach dem verschlüsselten String angehängt
            //(ist also nicht im Gegensatz zum Schlüssel nicht geheim)
            AES_encryptedString_textBox.Text = AesExample.AESByteArrayToString(encryptedByteArray); //
            aesAlgo.GenerateIV(); //Nach jedem Verschlüsseln, soll ein neuer Salt (IV) generiert werden
        }

Da wir den String nun verschlüsselt haben, möchten wir ihn nun wieder entschlüsseln. Dies geht mit folgender Methode. Die Methode erstellt ein neues Objekt der Klasse InsertOwnKey (Form), zeigt die Form an und bezieht aus den Properties der Klasse den Schlüssel (public byte[] Key). Zusätzlich ruft sie die eigentliche Methode zum Entschlüsseln auf (DecyrptAndShowString)

        private void AES_DecryptWithOwnKey()
        {
            InsertOwnKey insertOwnKey = new InsertOwnKey(AesExample.AESStringToByteArray(AES_salt_textBox.Text)); //Erstelle eine neue Instanz der InsertOwnKey-Klasse, welche von Form abgeleitet ist
            if (insertOwnKey.ShowDialog() == System.Windows.Forms.DialogResult.OK) //Rufe die Methode ShowDialog()-Methode der Instanz auf und prüfe ob der Rückgabetyp (DialogResult) OK ist
            {   //Sollte dies der Fall sein,
                aesAlgo.Key = insertOwnKey.Key; // so greife auf das öffentliche (public) Property 'Key' (Typ: ByteArray)
                //und ersetzte die Eigenschaft (Property) Key durch den Key aus der Instanz der InsertOwnKey-Klasse
                DecyrptAndShowString(); //Rufe die Methode zum Anzeigen des entschlüsselten Textes auf
            }
        }

        ///
/// Entschlüssele den verschlüsselten Text indem man den Text der TextBoxen in ein ByteArray wandelt und dies durch die AES-Klasse
        /// entschlüsseln lässt. Zur Entschlüsselung ist der verschlüsselte Text, der Schlüssel, sowie der Salt (Initialisierungvektor) erforderlich
        ///
        private void DecyrptAndShowString()
        {
            byte[] encryptedString = AesExample.AESStringToByteArray(AES_encryptedString_textBox.Text);  //Rufe eine Methode zur Wandlung eines Strings in ein ByteArray auf und übergibt das ByteArray in eine Variable
            byte[] salt = AesExample.AESStringToByteArray(AES_salt_textBox.Text); //Rufe eine Methode zur Wandlung eines Strings in ein ByteArray auf und übergibt das ByteArray in eine Variable
            string decryptedString = AesExample.DecryptStringFromBytes(encryptedString, aesAlgo.Key, salt); //Entschlüsselt die Daten und speichert sie in einer Variable zwischen
            if (!String.IsNullOrEmpty(decryptedString)) //Prüft ob die Variable zum Zwischenspeichern des entschlüsselten Strings NICHT (!) leer ist
            {
                MessageBox.Show(decryptedString); //Zeigt den entshlüsselten String in einer TextBox an
            }
        }

[/spoiler]
Folgende Klasse (Form) wird zur Erstellung des Schlüssels verwendet und über die Methode AES_CreateOwnSaltedKey aufgerufen.
[spoiler title=“Form zum Erstellen des Schlüssels“ style=“fancy“]
Um selbst ein Passwort zum Verschlüsseln zu verwenden, benötigt man die Rfc2898DeriveBytes-Klasse.

Diese verwende ich meiner eigenen Form, in Verbindung mit Aes-Klasse zum Erstellen eines Salts.

Die Klasse sieht dann wie folgt aus:

    public partial class CreateOwnSaltedKey : Form
    {
        Aes aesAlgo = Aes.Create();
        public byte[] Key { get; set; }
        public byte[] SALT { get; set; }

        public CreateOwnSaltedKey()
        {
            InitializeComponent();
            salt_textBox.Text = AesExample.AESByteArrayToString(aesAlgo.IV);
        }

        #region Random SALT
        private void createRandomSalt_button_Click(object sender, EventArgs e)
        {
            aesAlgo.GenerateIV();
            salt_textBox.Text = AesExample.AESByteArrayToString(aesAlgo.IV);
        }
        #endregion

        public void CreateKey()
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password_textBox.Text, aesAlgo.IV);
            Key = pdb.GetBytes(32);
            SALT = aesAlgo.IV;
        }

        #region Annehmen/Abbrechen
        private void abort_button_Click(object sender, EventArgs e)
        {
            this.DialogResult = System.Windows.Forms.DialogResult.Abort;
            this.Close();
        }

        private void accept_button_Click(object sender, EventArgs e)
        {
            CreateKey();
            this.DialogResult = System.Windows.Forms.DialogResult.OK;
            this.Close();
        }
        #endregion
    }

Für das Abrufen des Schlüssel, sowie des Salts (auch Initialisierungsvektor[IV] genannt) wurde zwei Properties vom Type Byte-Array erstellt. Diese sind public, d.h. es gibt keine Einschränkungen beim Zugriff auf diese Member und sie somit auch von anderen Klassen aus abrufbar.

[/spoiler]

Folgende Klasse (Form) bietet die Möglichkeit der Eingabe des Schlüssels, welcher zu Entschlüsselung benötigt wird. Ein Objekt der Klasse wird in der Methode AES_DecryptWithOwnKey erstellt und angezeigt.

[spoiler title=“Klasse zur Schlüssel-Eingabe zur Entschlüsselung“ style=“fancy“]

    public partial class InsertOwnKey : Form
    {
        public byte[] Key { get; set; }
        public byte[] SALT;

        public InsertOwnKey(byte[] pSALT)
        {
            InitializeComponent();
            SALT = pSALT;
        }

        private void createKey()
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password_textBox.Text, SALT);
            Key = pdb.GetBytes(32);
        }

        private void accept_button_Click(object sender, EventArgs e)
        {
            createKey();
            this.DialogResult = System.Windows.Forms.DialogResult.OK;
            this.Close();
        }

        private void abort_button_Click(object sender, EventArgs e)
        {
            this.DialogResult = System.Windows.Forms.DialogResult.Abort;
            this.Close();
        }
    }

[/spoiler]
[/tab]

[tab title=“Rijndael“]
Inhalt folgt noch. Der Code gleicht jenem der Aes-Klasse jedoch zu 90%
[/tab]
[/tabs]

Zu guter Letzt das Demo-Projekt:

20130811_Verweis-Manager - OleDBTest_000034

AES_Rijndael.zip

[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #2 – Verschlüsselung und SecureString

[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #1 – Plain

Im ersten Teil der Serie beschriebe ich, wie man eine einfach Authentifizierung mit hart-codierten oder im Klartext (Plain) gespeicherten Passwörtern erstellt. Nun möchte man das Passwort natürlich eher selten so offen und unsicher speichern. Im Folgenden geht es um das Verschlüsseln und Entschlüsseln, sowie das Sichern von Texten (Strings)

Verschlüsseln:

Als Paradebeispielt für eine unsichere Verschlüsselung gilt die Caesar-Verschlüsselung (hier ROT13), dabei wird ein Buchstabe einfach durch den 13 Stellen vor- oder hinter liegenden Buchstaben ersetzt.

    static class Rot13
    {
        ///
/// Verschiebe Buchstaben um 13 Stellen
        ///
        public static string Enrypt(string pString)
        {
            string rot13String = "";
            foreach (char c in pOrgString)
            {
                if (c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M')//falls der Buchstabe zwischen a/A und m/M liegt (m/M da es sich um den 13. Buchstaben handelt)
                {
                    rot13String += ((char)((int)c + 13)); //dann füge dem verschlüsselten String den Buchstaben, welcher 13 Stellen hinter dem   originalen Buchstaben steht hinzu
                }
                else if (c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z') //falls der Buchstabe zwischen n/N und z/Z liegt (n/N da es sich um den 14. Buchstaben also den ersten rotierten handelt)
                {
                    rot13String += ((char)((int)c - 13)); // dann ziehe 13 Stellen ab und füge den neune Char hinzu
                }
                else //Sollte es sich um keinen Buchstaben im lateinischen Alphabet handeln,
                {
                    rot13String += c; //So füge ihn unverändert hinzu
                }
            }
            return rot13String;
        }
    }

Entschlüsselt wird es indem man selben Code nochmals mit dem ROT13-verschlüsselten String als Parameter ausführt

Für die Verschlüsselung eines Strings mit AES (Rijndael-Algorithmus) verweise ich auf: [C#] AES-Ver und Entschlüsselung mit Rijndael

SecureString

Die Idee hinter dem SecureString ist es, die Zeit in der sich das Passwort (bzw. der zu sichernde Text) als Typ String im System befindet auf ein Minimum zu reduzieren. Dazu wird der String in ein Binärobjekt gewandelt und im unmanaged Memory (nicht verwalteter Speicher) gespeichert. Das führt dazu, dass das Objekt nicht durch den Garbage Collector berührt wird. Der SecureString wird fest im RAM verankert, sodass er nicht Teil der virtuellen Speichers ist und so „geswappt“ wird (Swap kann Daten auf der Festplatte hinterlassen, welche auch nach Beenden der Applikation noch vorhanden sind).
Früher oder später wird es nötig sein die Daten als String zwischenzuspeichern (beispielsweise, wenn der User das Passwort eingeben muss oder es wieder angezeigt werden soll).

Nun aber zum Code:
1. Erstellen eines SecureStrings:
Das Erstellen eines SecureString geschieht in der Regel mit der Methode AppendChar(char c). Zusätzlich gibt es noch die Methoden InsertAt(int index, char c), RemoveAt(int index) und SetAt(int index, char c).

        public void ConvertCharArrayToSecureString(char[] CharArray)
        {
            foreach (char c in CharArray) //IEnumerable
            {
                secureStr.AppendChar(c); //Für jeden Buchstaben (char) in dem String(Text in der Textbox), fügen wir den aktuellen Buchstaben dem SecureString an
            }
        }

Sobald das Passwort in den SecureString gewandelt wurde, solltet ihr den String/die TextBox leeren und den SecureString mittels der Methode MakeReadOnly() als schreibgeschützt festlegen.

Rufen wir nun die ToString() des SecureStrings auf und geben den Rückgabewert per MessageBox aus, so wird uns lediglich der Typ des Objekts angezeigt
2013-09-16 13_39_42-

2. Wandeln eines SecureStrings in einen String

Nun möchte man möglicherweise den SecureString doch auslesen. Dies lässt sich mit folgender Methode bewerkstelligen:

        public string ConvertSecureStringToString()
        {
            IntPtr unmanagedString = IntPtr.Zero; //Erstellt einen Zeiger (zeigt bisher auf nichts)
            try
            {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureStr); //kopiert den SecureString in den nicht verwalteten Speicher (dient als Puffer)
                return Marshal.PtrToStringUni(unmanagedString); //Reserviert einen verwalteten String (unseren Rückgabetyp) und kopiert eine nicht verwaltete Zeichenfolge (unser SecureString) hinein.
            }
            finally //Der im Finally-Block befindliche Code wirk auf jeden Fall ausgeführt (auch bei einer Exception)!
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); //Gibt den Zeiger, welcher mit SecureStringToGlobalAllocUnicode reserviert wurde, frei (Puffer wird befreit)
            }
        }

Bitte vergesst den Code im finally-Block nicht, denn ohne diesen entsteht eine Möglichkeit das Passwort auszulesen.

In unserem oberen Beispiel muss ich einen kompletten String bzw. ein komplettes CharArray übergeben, sodass es hier noch die Möglichkeit gibt, das komplette Passwort an einen Stück zu auszulesen, um dies noch weiter zu erschweren, kann man wie folgt vorgehen.

Man erstelle eine normale TextBox (in meinem Beispiel SecureString_SecurerTextBox_textBox genannt) und registriere einen Eventhandler für das KeyPress-Event. In der Methode des Events verwendet man dann folgenden Code:

private void SecureString_SecurerTextBox_textBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == '\b') //Falls die Rücktaste gedrückt wurde,
            {
                DeleteLastCharFromSecureString(); //soll der letzte Buchstabe des SecureStrings gelöscht werden
                if(SecureString_SecurerTextBox_textBox.Text.Length > 0) //Sowie der letzte Buchstabe des Textes in der TextBox, wenn dort noch mindestens ein Buchstabe vorhanden ist
                    SecureString_SecurerTextBox_textBox.Text = SecureString_SecurerTextBox_textBox.Text.Remove(SecureString_SecurerTextBox_textBox.Text.Length - 1, 1);
            }
            else //Sollte die Taste eines anderen Buchstaben gedrückt worden sein,
            {
                AddCharToSecureString(e.KeyChar); //rufe die Methode zum Anfügen des Buchstabens auf
                SecureString_SecurerTextBox_textBox.Text += "*"; //und füge dem Text der TextBox ein Sternchen an
            }
            e.Handled = true; //Bricht das KeyPress-Ereignis ab (es wird kein Buchstabe hinzugefügt)
        }

Folgende Methoden sind zusätzlich erforderlich:

        private void AddCharToSecureString(char c)
        {
            secureStr.AppendChar(c); //Fügt einen Buchstaben an letzter Stelle des SecureStrings an
        }

        private void DeleteCharFromSecureString()
        {
            if(secureStr.Length > 0)
                secureStr.RemoveAt(secureStr.Length - 1); //Löscht den letzten Buchstaben im SecureString
        }

Durch die Vorgehensweise wird der Buchstabe nur als Parameter übergeben und in der TextBox lediglich ein Sternchen (*) hinterlegt. Die Zeit in der sich das Passwort offen/komplett im Speicher liegt, ist stark reduziert. Leider leidet die Benutzerfreundlichkeit und die Übersichtlichkeit des Codes darunter.

Zu guter Letzt das Demo Projekt:

20130811_Verweis-Manager - OleDBTest_000034

ROT13 und SecureString