Abou Chleih

{the magic lies between the brackets}

Menü Schließen

Monat: Oktober 2013

[WP8] PDF-Datei aus dem Web laden und per App anzeigen

Eine PDF-Datei aus dem Web herunterzuladen und anzuzeigen ist relativ einfach.
Folgender Methoden laden die Webseite, laden die PDF-Datei herunter, speichern diese in den Isolated Storage und starten die externe App mit dem Pfad der Datei.

Folgende Links geben weitere Informationen zu folgendem Code:

WebClient pdfDownloader = null;
string LastFileName = ""; //Speichert den Dateinamen der zuletzt gesicherten Datei

private void StartPDFDownload()
{
    pdfDownloader = new WebClient(); //prevents that the OpenReadCompleted-Event is called multiple times
    pdfDownloader.OpenReadCompleted += DownloadPDF; //Create an event handler
    pdfDownloader.OpenReadAsync(new Uri("Your URL as string with HTTP://")); //Start to read the website
}

async void DownloadPDF(object sender, OpenReadCompletedEventArgs e)
{
    byte[] buffer = new byte[e.Result.Length]; //Gets the byte length of the pdf file
    await e.Result.ReadAsync(buffer, 0, buffer.Length); //Waits until the rad is completed (Async doesn't block the GUI Thread)

    using (IsolatedStorageFile ISFile = IsolatedStorageFile.GetUserStoreForApplication())
    {
        try
        {
            LastFileName = "tempPDF" + DateTime.Now.Ticks + ".pdf";
            using (IsolatedStorageFileStream ISFileStream = ISFile.CreateFile(LastFileName))
            {
                await ISFileStream.WriteAsync(buffer, 0, buffer.Length);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + Environment.NewLine + ex.HResult,
                ex.Source, MessageBoxButton.OK);
            //Catch errors regarding the creation of file
        }
    }
    OpenPDFFile();
}

private async void OpenPDFFile()
{
    StorageFolder ISFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
    try
    {
        IStorageFile ISFile = await ISFolder.GetFileAsync(LastFileName);
        await Windows.System.Launcher.LaunchFileAsync(ISFile);
            //http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206987%28v=vs.105%29.aspx
    }
    catch (Exception ex)
    {
        //Catch unknown errors while getting the file
        //or opening the app to display it
    }
}

[2009 R2 RTC] Reiter(ActionGroup) Abteilungen ausblenden

Mit dem Role-Tailored-Client hat sich in Dynamics NAV einiges verändert. So wurden rollenbasierte Menüs eingeführt, welche individuell für jede Rolle gestaltet werden können.
Standardmäßig ist jedem Menü auch der Reiter Abteilungen hinzugefügt. Hinter diesem verbergen sich sämtliche Pages aller Abteilungen, somit hat der Benutzer auch die Möglichkeit diese aufzurufen.

Dynamics NAV bietet aber die Möglichkeit den Reiter für bestimmte Rollen zu entfernen. In folgendem werde ich beschreiben, wie man den Reiter ausblendet.

Wir rufen den RTC-Client im Configuration-Mode auf. Dazu öffnen wir die Verknüpfung und fügen im Ziel den Parameter

-configure

sowie das Profil, in welchem der Reiter ausgeblendet werden soll (hier BUCHHALTER/IN TEST)

-profile:“BUCHHALTER/IN TEST“

an.

Das sollte das ungefährt so aussehen:

2013-10-25 11_32_10

Sollte nun beim Starten von Navision folgende Fehlmeldung auftauchen:

2013-10-25 11_34_03

So müsst ihr Rollenbasierter Client -> Profile dem gewählten Profil eine Besitzer-ID hinzufügen.
2013-10-25 11_37_12

Nachdem das erledigt ist, könnt ihr den RTC im Configuration-Mode starten und den Navigationsbereich anpassen:
2013-10-25 11_41_48

In Navigationsbereichs-Designer könnt ihr nun den Reiter „Abteilungen“ entfernen

2013-10-25 11_42_44

ACHTUNG: Ihr könnt den Reiter Abteilungen nicht einfach wieder hinzufügen! Dies ist nur möglich, indem man die Standardeinstellungen wiederherstellt. Dadurch gehen aber sämtliche sonstigen Änderungen ebenfalls verloren!

Nachdem ihr den Reiter gelöscht habt und mit OK bestätigt habt, müsst ihr lediglich euren Client neustarten.

[C#] XML in Applikation einlesen

Um schnell mal eine XML-Datei in die Applikation einzulesen, bedarf es dank des .NET-Frameworks nicht viel Arbeit.

Ich nutze dazu die XDocument-Klasse mit welcher es möglich ist XML zu parsen und zu schreiben.

Mein XML hatte folgende Form:

<?xml version="1.0" encoding="utf-8" ?>
<Phonenumbers>
  <phonenumber>
    <Name>Test</Name>
    <Number>12345678</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test2</Name>
    <Number>11111111</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test3</Name>
    <Number>222222222</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test4</Name>
    <Number>333333333</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test5</Name>
    <Number>44444444444</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test6</Name>
    <Number>55555555555</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test7</Name>
    <Number>66666666666</Number>
  </phonenumber>
  <phonenumber>
    <Name>Test8</Name>
    <Number>77777777777</Number>
  </phonenumber>
</Phonenumbers>

In die Applikation wird das XML mit folgendem Code geladen:

string xml = System.IO.File.ReadAllText(PfadZurXMLDatei);
XDocument xmlReader =  XDocument.Parse(xml);
var List = from element in xmlReader.Descendants("phonenumber")
		select new
		{
			name = (string)element.Element("Name"),
			number = (string)element.Element("Number"),
			//Weitere Elemente lesen
		};
foreach (var item in List)
{
	listBox1.Items.Add(item.name);
	listBox2.Items.Add(item.number);
}

[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #4 – ActiveDirectory

[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #1 – Plain
[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #2 – Verschlüsselung und SecureString
[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #3 – MySQL und Hash

ActiveDir_auth
Bei Active Directory (ab Windows Server 2008 Active Directory Domain Services) handelt es sich um einen Verzeichnisdienst (englisch directory service) von Microsoft. Sie speichert Benutzerinformationen(Benutzernamen, Kennwort sowie Gruppen) zentral auf einer Datenbank und stellt diese den Clients im Netzwerk zur Verfügung. Dadurch ist es, im Gegensatz zu einer Arbeitsgruppe, möglich die Benutzerdaten auf jeden Rechner in der Domäne zu verteilen. Somit ist es nicht mehr nötig jedes Profil auf jedem Rechner einzurichten, was eine enorme Zeitersparnis ergibt.

In vielen Firmen wird Active Directory genutzt und eine Authentifizierung ist  mit C# in Verbindung mit dem .NET-Framework schnell geschrieben.
Ich werden hier beschreiben, wie sich ein Benutzer durch Eingabe seines Benutzernamens und Kennworts authentifizieren kann, sowie die Überprüfung, ob sich der Benutzer in einer Gruppe befindet.

Authentifizierung über Benutzernamen und Kennwort:

Zuerst widmen wir uns der Authentifizierung an einer Domäne mittels Benutzername und Passwort.
Mit folgendem Code beziehen wir die aktuell verwendete Domäne aus dem System.

System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;

Die einfachste Variante ergibt sich mit der Verwendung von PrincipalContext-Klasse, welche sich im Namespace System.DirectoryServices.AccountManagement befindet

public static bool? AuthenticateUserWithAD(string Domain, string Username, string Password)
{
	try
	{
		using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain))
		{
			//Überprüft die übergebenen Anmeldedaten mit dem angegeben ContextType (ApplicationDirectory, Domain oder Machine)
			return pc.ValidateCredentials(Username, Password); //Gibt true oder false zurück
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message, ex.Source);
		return null;
	}
}

Wenn man nicht auf PrincipalContext zurückgreifen möchte, so kann man den Benutzer auch mittels DirectoryEntry aus dem Namespace System.DirectoryServices authentifizieren.

Dazu verwendet man folgenden Methode:

private static readonly int ERROR_DS_NO_SUCH_OBJECT = -2147016656; //Fehlercode für LDAP_NO_SUCH_OBJECT 0x80072030 | http://msdn.microsoft.com/en-us/library/aa746528(v=vs.85).aspx
public static DirectoryEntry GetDirectoryEntry(string Domain, string username, string password)
{
	DirectoryEntry dirEntry = new DirectoryEntry();
	dirEntry.Path = "LDAP://" + Domain;
	dirEntry.Username = username;
	dirEntry.Password = password;

	try
	{
		if (dirEntry.NativeObject == null)
		{
			// Kein ActiveDir-Objekt gefunden
			dirEntry = null;
		}
	}
	catch (COMException ex)
	{
		if (ex.ErrorCode == ERROR_DS_NO_SUCH_OBJECT)
		{
			MessageBox.Show("Es wurde kein Objekt, welches mit den Eingaben übereinstimmt gefunden", "Fehler");
		}
		else
		{
			MessageBox.Show(ex.ErrorCode.ToString() + "\r\n" + ex.Message);
		}
		dirEntry.Close();
		dirEntry = null;
	}
	return dirEntry;
}

Um nun einen Wahrheitswert (Boolean) zurückzugeben, muss man prüfen, ob das DirectoryEntry-Objekt gefüllt oder null ist.
Dazu verwendet man folgende Methode, welche wir später dann auch über unsere Klasse aufrufen.

public static bool AuthenticateUser(string Domain, string username, string password)
{
	return GetDirectoryEntry(Domain, username, password) != null;
}

Falls man nur wissen möchte, ob der Benutzer existiert, so kann man ebenfalls PrincipalContext verwenden, jedoch diesmal in Verbindung mit der statischen Methode FindByIdentity der UserPrincipal-Klasse

public static bool CheckIfUserExists(string Domain, string Username)
{
	using (var pc = new PrincipalContext(ContextType.Domain, Domain))
	{
		using (var User = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, Username))
		{
			return User != null;
		}
	}
}

Überprüfung, ob sich Benutzer in Gruppe befindet:

Nun möchte man natürlich nicht immer den Benutzer authentifizieren oder prüfen, ob dieser existiert, sondern spezielle Funktionen einem gewissen Kreis von Personen bereitstellen.

Dazu bietet ActiveDirectory s.g. Gruppen

Eine Gruppe besteht aus Benutzer- und Computerkonten, Kontakten und anderen Gruppen, die als eine Einheit verwaltet werden können. Benutzer und Computer, die zu einer bestimmten Gruppe gehören, werden als Gruppenmitglieder bezeichnet.
Die Verwendung von Gruppen vereinfacht die Verwaltung, indem vielen Konten in einem Schritt einheitliche Berechtigungen und Rechte zugewiesen werden können, anstatt jedem Konto einzeln Berechtigungen und Rechte zuweisen zu müssen.

Für die Prüfung, ob der Benutzer Mitglied der Gruppe ist, gibt es zwei Möglichkeiten.

  1. Unter Verwendung von DirectoryEntry
  2. Benutzung WindowsIdentity

Zuerst unter Verwendung von DirectoryEntry

public static bool? CheckIfUserIsInGroupWithDirectoryEntry(string Domain, string Username, string Password, string Group)
{
	if (Username == "" || Password == "")
	{
		return false;
	}

	try
	{
		DirectoryEntry entry = new DirectoryEntry("LDAP://" + Domain, Username, Password);
		DirectorySearcher mySearcher = new DirectorySearcher(entry);
		mySearcher.Filter = "(&(objectClass=user)(|(cn=" + Username + ")(sAMAccountName=" + Username + ")))";
		SearchResult result = mySearcher.FindOne();

		foreach (string GroupPath in result.Properties["memberOf"])
		{
			if (GroupPath.Contains(Group))
			{
				return true;
			}
		}
	}
	catch (DirectoryServicesCOMException DirSvrComEx)
	{
		MessageBox.Show(DirSvrComEx.Message);
		return null;
	}
	return false;
}

Und die Alternative mit WindowsIdentity:

public static bool? CheckIfUserIsInGroup(string Username, string Groupname)
{
	try
	{
		using (var identity = new WindowsIdentity(Username))
		{
			var principal = new WindowsPrincipal(identity);
			return principal.IsInRole(Groupname);
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
		return null;
	}
}

Möchte man den aktuellen Benutzer verwenden (und somit keine Eingabe der Login-Informationen erfordern), so kann man die Methode wie folgt anpassen

public static bool? CheckIfCurrentUserIsInGroup(string Groupname)
{
	try
	{
		IntPtr accountToken = WindowsIdentity.GetCurrent().Token;
		using (var identity = new WindowsIdentity(accountToken))
		{
			var principal = new WindowsPrincipal(identity);
			return principal.IsInRole(Groupname);
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
		return null;
	}
}

Zu guter Letzt das Demo-Projekt:

20130811_Verweis-Manager - OleDBTest_000034

ActiveDirectory_Auth.zip

[WordPress] Integrieren des WordPress-Blog in die eigene Homepage #2 Suchfunktion

Integrierung des eigentlichen Blogs findet hier in Part1.

Nun haben wir den Blog mit all seinen Posts eingefügt. Es fehlen allerdings noch ein paar essentielle Features eines jeden Blogs.
Beispielsweise die Suchfunktion, um welche es in diesem Post gehen wird.

Um eine Suche starten zu können benötigen wir natürlich zuerst einmal ein Suchfeld.
In HTML werden Eingabefelder (Textboxen, …) mit <input …>-Tags erstellt.
In unserem Fall einem Textfeld sieht dies folgendermaßen aus:

 <input name="s" type="text" size="20" maxlength="20"> 

Der Bezeichner des Parameters ist hier „s“ (ist bei WordPress Pflicht!!), der Typ ist „text“ also Textfelder und die maximale Eingabelänge beträgt 20 Zeichen.
Nun benötigen wir noch einen Button, welcher eine Aktion (hier: die Suche) auslösen soll.
Dafür gibt es in HTML den Typ „submit“:

 <input type="submit"> 

Sobald man nun auf den Knopf drückt, passiert … nichts.
Wieso? … Man muss dem Browser noch sagen, was beim Drücken des Knopfes passieren soll, welche Aktion also ausgeführt werden soll.
In meinem Beispiel soll die search.php-Seite aufgerufen werden.

 <form action="search.php"> input tags here </form>

Nun wird beim Drücken des Buttons die search.php aufgerufen und der Parameter (hier: s), als Post-Parameter (also in der Browserleiste) übergeben.
Das Ergebnis sieht so aus:

http://www.meineDomainHier.de/search.php?s=MeinSuchtext

Nun zum eigentlichen Part, der Such-Page search.php.
Der Aufbau ähnelt sehr stark dem eigentlich Blog. Auch hier wird die Loop verwendet.

  <?php if (have_posts()) : ?>
         <p class="info">Deine Suchergebnisse f&uuml;r '<strong><?php echo $s ?></strong>'</p> <-- Deine Suchergebnisse fuer XYZ --!>
		 <br/>
         <?php while (have_posts()) : the_post(); //The Loop ?> 

In dieser Loop kann man dann wieder, wie im Blog, die einzelnen Daten, welche man benötigt abrufen.
Ich beschränke mich jetzt hier mal auf den Titel und den Inhalt bzw. eine Vorschau dessen.
Die Ausgabe erfolgt hier per PHP wie folgt:

 <?php the_title();
<?php the_excerpt(); // Vorschautext oder the_content(); ?>

Nun schließt man die while-Schleife noch:

 <? endwhile; ?> 

Sollte kein Ergebnis gefunden worden sein, so wird der else-Zweig der if-have-post-Abfrage ausgeführt:

  <?php else : ?>
         <p class="info">Leider wurde f&uuml;r den Suchbegriff '<strong><?php echo $s ?></strong>' nichts gefunden</p>
      <?php endif; ?> 

Damit ist die Suchfunktion fertig.

[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #3 – MySQL und Hash

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

Eine Authentifizierung mittels MySQL ist ziemlich einfach und schnell geschrieben.

Wir müssen lediglich den .NET Connector für MySQL herunterladen, den Verweis auf die DLL hinzufügen und folgenden Namespace mittels

using MySql.Data.MySqlClient;

einbinden.

Nun benötigen wir ein Objekt der MySQLConnection, welches den ConnectionString beinhaltete und eine Verbindung zum MySQL-Server bereitstellt.

Ich erstelle ein Field vom Typ MySQLConnection (also eine Variable, auf welche alle Methoden innerhalb der Klasse zugreifen können), welches jedoch noch nicht initialisiert wurde.

MySqlConnection connection = null;

In der öffentlichen Methode setupConnection() erstelle ich den ConnectionString (mit den Parametern) und initialisiere den MySQLConnector mit dem ConnectionString.

public void setupConnection(string server, string database, string user, string password)
{
     string myConnectionString = "SERVER=" + server + ";" +
     "DATABASE=" + database + ";" +
     "UID=" + user + ";" +
     "PASSWORD=" + password + ";";
     connection = new MySqlConnection(myConnectionString);
}

Nun erstelle ich eine weitere öffentliche Methode, welche zur Authentifizierung dient.

public bool? Authenticate(string username, string password, string table) //Das Fragezeichen (?) nach einem Typ gibt an, dass der <a href="http://msdn.microsoft.com/de-de/library/1t3y8s4s(v=vs.90).aspx" target="_blank">Typ NULL-Werte zulässt (nullable)</a>
{
	try
	{
		MySqlCommand command = connection.CreateCommand();
		command.CommandText = "SELECT * FROM "+ table +" WHERE Username=?Username"; //Erstellt die parametrisierte Abfrage (Tabellen können nicht parameterisiert werden)
		command.Parameters.AddWithValue("?Username", username); //Weißt dem Parameter einen Wert zu
		MySqlDataReader Reader;
		connection.Open(); //Öffnet die Verbindung zum Server
		Reader = command.ExecuteReader(); Führt die Abfrage auf dem Server aus
		while (Reader.Read()) //Solange noch Daten verfügbar sind
		{
			if (Reader[1].ToString() == username) //Prüfe ob der Wert der ZWEITEN(!) Spalte mit dem übergebenen Usernamen übereinstimmt
			{ //Falls dies der Fall ist,
				if (Reader[2].ToString() == password)//prüfe ob der Wert der dritten Spalte mit dem Passwort übereinstimmt
				{//Sollte dem so sein,
					connection.Close(); //so schließe die Verbindung
					return true; //und geben den Wahrheitswert "wahr" zurück
				}
			}
		}
                //Sollten die Parameter nicht mit den Daten aus der DB übereingestimmt haben
		connection.Close(); //So schließe die Verbindung
		return false; //Und geben "false" zurück
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
		connection.Close();
		return null;
	}
}

Hashing

Nun ist es natürlich äußerst fahrlässig Passwörter klar in der Datenbank zu hinterlegen. Im besten Fall soll es unmöglich sein, das Passwort wieder im Klartext aus der Datenbank zu beziehen und genau hier wird Hashing genutzt. Im Gegensatz zur Verschlüsselung ist es dabei nicht mehr möglich das gehashte Passwort in das ursprüngliche zurückzuwandeln.
Es gibt ziemlich viele Hashfunktionen, zwei der Bekanntesten sind MD5 und SHA.

[row][column size=“1/2″]MD5[/column] [column size=“1/2″]SHA-2[/column][/row]

[row][column size=“1/4″]Vorteile[/column] [column size=“1/4″]Nachteile[/column] [column size=“1/4″]Vorteile[/column] [column size=“1/4″]Nachteile[/column][/row]
[row][column size=“1/4″]Schnell[/column] [column size=“1/4″]Nicht kollisionsresistent[/column] [column size=“1/4″]Bisher nicht geknackt[/column] [column size=“1/4″]Langsamer als MD5[/column][/row]
[row][column size=“1/4″][/column] [column size=“1/4″]Viele Regenbogentabellen[/column] [column size=“1/4″]Gilt als kollisionsresistent[/column] [column size=“1/4″][/column][/row]

Die Schnelligkeit beider Hashfunktionen ist aber auch eine der größten Schwächen in Bezug auf die Sicherheit des Passworts. So ist heutzutage möglich ein 8-stelliges Passwort, bestehend aus Zahlen und Buchstaben, gehasht mit SHA-2 GPU-gestützt innerhalb eines Tages zu knacken.

MD5

Nun aber zur Implementierung einer MD5-Hashfunktion mit C# und dem .NET-Framework. Dazu verwenden wir die MD5CryptoSerivceProvider-Klasse

public string CreateMD5Hash(string unhashedString)
{
	if (String.IsNullOrEmpty(unhashedString))
		return string.Empty;

	MD5 md5 = new MD5CryptoServiceProvider(); //Alternativ kann man auch MD5 md5 = MD5.Create(); verwenden
        //Create() initalisiert MD5CryptoServiceProvider, da MD5CryptoServiceProvider MD5 implementiert. MD5 ist eine abstrakte Klasse
	byte[] unhashedByteArray = Encoding.Default.GetBytes(unhashedString);
	byte[] result = md5.ComputeHash(unhashedByteArray);

	return System.BitConverter.ToString(result).ToLower().Replace("-", "");
}

SHA-2

Beinahe den selben Code kann man für SHA-2 verwenden. Statt dem MD5CryptoServiceProvider verwenden wir die SHA256CryptoServiceProvider-Klasse

public string CreateSHA2Hash(string unhashedString)
{
	if (String.IsNullOrEmpty(unhashedString))
		return string.Empty;

	SHA256 sha2 = new SHA256CryptoServiceProvider();
	byte[] unhashedByteArray = Encoding.Default.GetBytes(unhashedString);
	byte[] result = sha2.ComputeHash(unhashedByteArray);

	return System.BitConverter.ToString(result).ToLower().Replace("-", "");
}

Und jetzt haben wir eine Todsünde begangen. Das Hashes sind nicht gesalzen und können somit einfach per Rainbow Tables geknackt werden.

Salted MD5

Genau für diesen Zweck wurde HMAC entwickelt. Ein Implementierung in das .NET-Framework erfolgte mit der HMACMD5-Klasse

public string CreateSaltedMD5Hash(string unhashedString, byte[] key)
{
	HMACMD5 saltedMD5 = new HMACMD5();

	saltedMD5.Key = key;

	byte[] unhashedByteArray = Encoding.Default.GetBytes(unhashedString);
	byte[] result = saltedMD5.ComputeHash(unhashedByteArray);

	return System.BitConverter.ToString(result).ToLower().Replace("-", "");
}

Salted SHA-2

Für SHA2 gibt es die HMACSHA256-Klasse

public string CreateSaltedSHA2Hash(string unhashedString, byte[] key)
{
	HMACSHA256 saltedSHA2 = new HMACSHA256();

	saltedSHA2.Key = key;
	byte[] unhashedByteArray = Encoding.Default.GetBytes(unhashedString);
	byte[] result = saltedSHA2.ComputeHash(unhashedByteArray);

	return System.BitConverter.ToString(result).ToLower().Replace("-", "");
}

Wie ich oben bereits erwähnt habe, ist die Geschwindigkeit die größte Schwäche dieser Algorithmen. Darum sollte man MD5 und SHA auch mit Salt nicht zum Hashen von Passwörtern verwenden (MD5 und SHA dienen zur Berechnung einer eindeutigen Prüfsumme, nicht zum Hashen von Passwörtern). Für diesen Zweck wurden langsamere Algorithmen entwickelt, welche generell für das Verschlüsseln von Passwörtern verwendet werden sollten.  Die Bekanntesten sind PBKDF2 und bcrypt.

Zu guter Letzt das Demo-Projekt:

20130811_Verweis-Manager - OleDBTest_000034

MySQL_Hash.zip

[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

© 2019 Abou Chleih. Alle Rechte vorbehalten.

Thema von Anders Norén.