Abou Chleih

{the magic lies between the brackets}

Menü Schließen

Autor: Daniel Abou Chleih (Seite 2 von 4)

[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

[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

[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

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

Im Klartext hinterlegte Passwörter sind natürlich in keinster Weise zu empfehlen, da sie leicht ausgelesen werden können. Dennoch sind die für kleinere Programme, welche unkritisch sind manchmal sinnvoll, da solch ein Passwortschutz schnell geschrieben ist und seinen Zweck allemal erfüllt.
Ein mir bekanntes Beispiel wäre ein Textticker, in welchem man den Pfad nur mittels Passworteingabe ändern kann (solch ein Programm ist sehr unkritisch und bedarf keines extra Schutzes. Zudem verhindert solch ein Passwort, dass unbedarfte Benutzer die Einstellungen (un)absichtlich verändern).

Hartcodiert

Ist wohl die einfachste Möglichkeit ein Passwortschutz zu integrieren.
Dazu benötigt man lediglich zwei Strings(Username/Passwort) und legt diese wie gewünscht fest. Bspw. Username:Root, Passwort:tooR
In der Credentialsabfrage vergleichen wir dann einfach die hinterlegten mit jenen, welche eingegeben wurden.
Folgender Code wird dafür benötigt:

        string login_user = "Root"; //Hartcodierter Eintrag des Users
        string login_pw = "tooR"; //Hartcodierter Eintrag für das Passwort

        public Form1()
        {
            InitializeComponent();
            auth_button.Click += new EventHandler(auth_button_Click); //Eventhandler für den Click auf den Authentifizierungbutton
        }

        private void auth_button_Click(object sender, EventArgs e)
        {
            if (authenticate() == true) //Aufruf der Methode zum Vergleich der eingetragenen Werte mit den hinterlegten
                MessageBox.Show("Erfolgreich authentifiziert");
            else
                MessageBox.Show("Anmeldung war nicht erfolgreich");
        }

        private bool authenticate() //Rückgabetyp boolean
        {
            if (user_textBox.Text == login_user && pw_textBox.Text == login_pw) //Überprüfung der Gleichheit der Werte
                return true; //Gibt true zurück, falls die Werte gleich sind
            else //Ansonsten false
                return false;
        }

In Datei klar gespeichert

Ist eigentlich das selbe Spiel, jedoch weisen wir den Strings nicht „sofort“ einen Werte zu, sondern lesen sie zuerst aus einer Datei aus und dann weisen wir sie zu.
Da sich dieser Variante nur durch das Auslesen unterscheidet, werde ich nur eben jenes beschreiben.

Zuerst fügen wir den Namespace System.IO; mittels:

using System.IO;

hinzu und rufen folgende Methode beim Starten des Programms(im Konstruktor der Main-Klasse) auf

        private void GetLoginValues()
        {
            int lineCount = 1; //Wird benötigt, um die aktuelle Zeile zu ermitteln
            StreamReader credValueReader = new StreamReader("credValue.txt"); //Hier wird das Objekt angelegt und der Pfad festgelegt
            //in diesem Fall muss die Datei im Verzeichnis des Programmes liegen
            while (!credValueReader.EndOfStream) //Solange nicht das Ende der Datei erreicht wurde
            {
                switch (lineCount)
                {
                    case 1:
                        login_user = credValueReader.ReadLine(); //Weißt dem Userstring den Wert der ersten Zeile zu
                        break;
                    case 2:
                        login_pw = credValueReader.ReadLine(); //Weißt dem Passwortstring den Wert aus der nächsten Zeile(2) zu
                        break;
                }
                lineCount++; //erhöht den Zeilenzähler
            }
            credValueReader.Close(); //Löst den Zugriff auf die Datei(ist sehr wichtig!)
        }

[C#] Sortieren einer DataTable-Spalte nach einer Spalte eines anderen DataTable

Ich hatte die Aufgabe eine Spalte einer DataTable bzw. die DataTable(1) an sich nach der Spalte einer anderen DataTable(2) zu sortieren ohne die Reihenfolge der zweiten DataTable(2) zu verändern.
Um zu verdeutlichen, wie die Aufgabenstellung genau aussah, habe ich hier Screenshots der DataTables visualisiert in einem DataGridView im Ist/Soll-Zustand:

Die Sort-Methode des DataGridView, ebenso wie die Sort-Eigenschaft eines DataViews,

DataTable.DefaultView.Sort = "Spaltenname ASC"

bieten nur die Möglichkeit eine Spalte auf(Ascending)- bzw. absteigend(Descending) zu sortieren.

So musste ich mir dann einen eigenen Weg finden, die Aufgabe zu lösen. Die Lösung möchte ich euch natürlich nicht vorenthalten.

 private void sortList(string pExcelDataColumnName, string pSortByListColumnName)
        {
            excelDataTable.Columns.Add("TempOrderColumn", typeof(Int32)); //Dient später zum Sortieren der Zeilen

            foreach (DataRow row in excelDataTable.Rows)
            {
                foreach (DataColumn column in excelDataTable.Columns)
                {
                    if (column.ColumnName == pExcelDataColumnName)
                    {
                        object tempRowIndex = GetRowIndexOfCellValue(row[column], pSortByListColumnName);
                        row["TempOrderColumn"] = tempRowIndex;
                    }
                }
            }

            DataView dv = excelDataTable.DefaultView;
            dv.Sort = "TempOrderColumn ASC";
            excelDataTable = dv.ToTable();
            excelData_dataGridView.Refresh();
        }

In dieser Methode erstellen wir erstmal eine weitere Spalte, welche uns später zum Sortieren dient, danach durchlaufen wir alle Zellen der unsortierten Liste(zweimal foreach) und fragen ab, ob sich einer der Zellen in der zuvor definierten Spalte befindet(Parameter der Methode).
Sollte dies der Fall sein, so rufen wir folgende Methode auf und übergeben den Inhalt der aktuellen Zelle, sowie den Namen der zu vergleichenden Spalte:

private object GetRowIndexOfCellValue(object pCellValue, string pSortByListColumnName)
        {
            foreach (DataRow row in sortByDataTable.Rows)
            {
                foreach (DataColumn column in sortByDataTable.Columns)
                {
                    if (column.ColumnName == pSortByListColumnName && pCellValue.Equals(row[column]))
                    {
                        return sortByDataTable.Rows.IndexOf(row);
                    }
                }
            }
            return -1;
        }

Diese Methode durchläuft sämtliche Zellen der Liste, nach welcher wir sortieren wollen und prüft ob sich die Zelle in der als Parameter übergeben Spalte befindet(indem sie den übergeben Spaltennamen mit jenem der aktuellen Spalte(Column) vergleicht) und ob der Inhalt der Zelle mit jenem Inhalt übereinstimmt, welcher als Parameter übergeben wurde.

Sollte beide Abfragen ‚wahr‘ sein, so geben wir den Index der aktuellen Zeile zurück. Dafür verwenden wir die folgende Methode

return sortByDataTable.Rows.IndexOf(row);

Der Rückgabewert wird dann mittels

row["TempOrderColumn"] = tempRowIndex;

in die temporäre Spalte geschrieben und zu guter Letzt mit folgendem Code sortiert:

            DataView dv = excelDataTable.DefaultView;
            dv.Sort = "TempOrderColumn ASC";
            excelDataTable = dv.ToTable();
            excelData_dataGridView.Refresh();

Dies ist nötig, da man einer DataTable nicht direkt sortieren kann und somit den Umweg über das Erstellen eines DataViews gehen muss, diesen dann mittels

dv.Sort = "Spaltenname ASC" //ASC steht für 'ascending' aufsteigend
//DESC steht für 'descending' absteigend

sortiert und das DataView dann wieder in die DataTable wandelt.

Das Ergebnis sieht wie folgt aus:
2013-08-23 15_51_20-Excel nach Liste sortieren

Nun muss ich nur noch eine Excel-Tabelle aus der DataTable bzw dem DataGridView erstellen. Wie man dies bewerkstelligt, zeige ich in meinem nächsten Blogeintrag

[C#] Auslesen einer Excel Datei #2 – OleDB

Teil 1: [C#] Auslesen einer Excel Datei #1 – Interop-Assembly

OleDB steht für Object Linking and Embedding, Database und bietet einem im Gegensatz zu Interop-Assembly die Möglichkeit auf verschiedenste Datenquellen (bspw. auch auf Datenbanken zuzugreifen). Zugleich muss im Gegensatz zu Interop-Assembly kein Microsoft Excel installiert sein und die meisten Controls, wie z.B. DataGridView, akzeptieren out-of-the-box die Datenquelle (Data Source) welche OleDB bietet.

Hier ein Zitat aus dem MSDN:

OLE DB is a set of COM-based interfaces that expose data from a variety of sources. OLE DB interfaces provide applications with uniform access to data stored in diverse information sources, or data stores. These interfaces support the amount of DBMS functionality appropriate to the data store, enabling the data store to share its data.

OleDB hat einige Vorteile gegenüber Interop-Assembly:

  • Microsoft Excel muss nicht installiert sein
  • Es ist um einiger schneller als Interop

Natürlich aber auch Nachteile

  • OleDB bietet im Vergleich zu Interop in Bezug auf Excel weniger Möglichkeiten, vor allem bezüglich Zelleninformationen, wie Farbe, Rahmen oder Formatierung

Nun aber zur Nutzung von OleDB

Normalerweise bindet Visual Studio automatisch System.Data-Assembly ein und wir müssen nur noch den Namespace mittels

using System.Data.OleDb;

einbinden

Sollte es dennoch zu folgender Fehlermeldung

Fehler 1

Der Typ- oder Namespacename „OleDb“ ist im Namespace „System.Data“ nicht vorhanden. (Fehlt ein Assemblyverweis?)

müssen wir einen Assemblyverweis erstellen.

Dazu einfach mit der rechten Maustaste auf „Verweise“ klicken:

20130811_OleDBTest - Microsoft Visual Studio_000033

„Verweis hinzufügen“ wählen und in den Framework-Assemblys nach System-Data suchen und diese Assembly dann wählen.20130811_Verweis-Manager - OleDBTest_000034

Zu guter Letzt noch mit „OK“ bestätigen.

Ich persönlich bevorzuge es Objekten mittels using eine definierte Gültigkeit zuzuweisen, dies bedeutet, dass das in der Using-Anweisung definierte Objekt nach dem Verlassen der Anweisung nicht mehr benötigt wird und zerstört werden kann.

Dadurch ist es aber nötig den ConnectionString, vor dem Erstellen der OleDbConnection zu definieren und per Paramter zu übergeben.
Um einen ConnectionString zu erstellen, verwende ich den OleDbConnectionStringBuilder:

Dieser bietet eine einfache Möglichkeit, den Inhalt von Verbindungszeichenfolgen zu erstellen und zu verwalten, die von der OleDbConnection-Klasse verwendet werden.


OleDbConnectionStringBuilder csbuilder = new OleDbConnectionStringBuilder();
 csbuilder.Provider = "Microsoft.ACE.OLEDB.12.0";
 csbuilder.DataSource = "Pfad zu Excel-Datei";
 csbuilder.Add("Extended Properties", "Excel 12.0 Xml;HDR=YES");

Der Provider ist eine Softwarebibliothek, welche die nötigen Werkzeuge bietet, um auf eine Datenquelle zuzugreifen. Im ConnectionString werden die Informationen, welche den Provider in die Lage versetzen auf die Quelle zuzugreifen, hinterlegt.
Für ConnectionStrings empfehle ich: http://www.connectionstrings.com/

Im Property DataSource wird der Pfad zu Excel-Datei angegeben.

Mittels der Methode Add werden die Informationen hinterlegt, welche den Typ der Datei und Informationen zum Inhalt definieren.
In diesem Beispiel wird hinterlegt, dass es sich um eine Excel-Datei handelt, welche mit Excel 2010 erstellt wurde(Excel 12.0) und deren erste Zeile Überschriften(Header/HDR) sind.

Da nun die wichtigsten Eigenschaften definiert wurden, können wir das erste Tabellenblatt der Excel-Datei auslesen.
Dazu verwenden wir die SQL-Abfrage

SELECT * FROM [“ + Name_des_Tabellenblattes + „]

(Die eckigen Klammern werden zwingend benötigt)

             using (OleDbConnection connection = new OleDbConnection(csbuilder.ConnectionString))
            {
                DataTable sheet1 = new DataTable();
                connection.Open();
                string sqlQuery = @"SELECT * FROM [" + Name_des_Tabellenblattes + "]";
                using (OleDbDataAdapter adapter = new OleDbDataAdapter(sqlQuery, connection))
                {
                    adapter.Fill(sheet1);
                    excelData_dataGridView.DataSource = sheet1;
                }
                connection.Close();
            }

Und erstellen ein Objekt vom Typ OleDbDataAdapter mit den Parametern „SQL-Query“ und dem ConnectionString.
Danach füllen wir den DataTable mittels adapter.Fill(Name_des_DataTables) und füllen ein DataGridView mittels

DataGridView.DataSource = Name_des_DataTables;

Namen der Tabellenblätter auslesen

Nun ist natürlich ungeschickt den Namen des Tabellenblattes hartcodiert im Code zu hinterlegen.
Um die Tabellenblattnamen auszulesen und in ein StringArray zu laden verwenden wir folgenden Code:

OleDbConnectionStringBuilder csbuilder = new OleDbConnectionStringBuilder();
csbuilder.Provider = "Microsoft.ACE.OLEDB.12.0";
csbuilder.DataSource = pExcelPath;
csbuilder.Add("Extended Properties", "Excel 12.0 Xml;HDR=YES");

System.Data.DataTable dt = null;

using (OleDbConnection connection = new OleDbConnection(csbuilder.ConnectionString))
{
    connection.Open();
    dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
    connection.Close();
}

String[] excelSheets = new String[dt.Rows.Count];

int i = 0;
foreach (DataRow row in dt.Rows)
{
    excelSheets[i] = row["TABLE_NAME"].ToString();
    i++;
}

Mit dem StringArray kann man nun eine ganze Menge anfangen.
Ich habe beispielsweise das StringArray als Parameter an ein Objekt einer Form übergeben, welches jeden String im Array(mittels foreach-Schleife) in eine ListView kopiert und man so das gewünschte Tabellenblatt auswählen kann.

DemoProjekt

Hier könnt ihr das Demoprojekt herunterladen:

20130811_Verweis-Manager - OleDBTest_000034

OleDBTest

[C#] Serialisieren von Listen (eigener Objekte)

In manchen Fällen kann es sinnvoll sein Listen und deren Inhalt zu speichern.
In meinem Fall wurde die Liste mit Objekten einer eigens erstellen Klasse gefüllt, dadurch ist es sehr schwierig diese Liste mit dem XmlSerializer zu serialisieren/speichern.

Für solche Fälle eignet sich der BinaryFormatter

Um die Daten zu serialisieren benötigen wir folgende Namespaces:

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization; //Kann fürdas Exception-Handling genutzt werden
//bspw. SerializationException

Zusätzlich benötigt man natürlich auch das zu sichernde Objekt – in meinem Fall eine Liste mit Objekten vom Typ „Receiver“ mit Namen receiverList

List<Receiver> receiverList = new List<Receiver>();

Meine Receiver-Klasse sieht wie folgt aus:

    [Serializable()]
    public class Receiver
    {
        public string Name { get; set; }
        public string MACAddress { get; set; }
        public IPAddress IP { get; set; }
        public string Info { get; set; }

        public Receiver(string pName, string pMAC, string pIPAddress, string pInfo)
        {
            Name = pName;
            MACAddress = pMAC;
            if (pIPAddress != null && pIPAddress != "")
            {
                IP = IPAddress.Parse(pIPAddress);
            }
            Info = pInfo;
        }
    }

Und schon haben wir den ersten wichtigen Punkt: Die Klasse muss als serialisierbar(engl. Serializable) gekennzeichnet sein, daher muss das Serializable-Attribut [Serializable()] über dem Header der Klasse angebracht werden, eine paramterlosen Konstruktor, wie bei XmlSerializer benötigt man nicht

Nun zum eigentlichen Speichern der Liste, man könnte natürlich auch jedes Objekt einzeln speichern, jedoch halte ich das für aufwendiger und man benötigt die genaue Anzahl der zu serialisierenden Objekte. Deshalb speichern wir die Collection selbst und schreiben sie komplett in einen FileStream (Datenstrom).

        private void SaveReceiverList()
        {
            FileStream stream;
            stream = new FileStream(@"receiverList.dat", FileMode.Create);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, receiverList);
            stream.Close();
        }

Zum Laden der Liste benutzen wir folgenden Code:

        private void LoadReceiverList()
        {
            FileStream fs = new FileStream(@"receiverList.dat", FileMode.Open); ;
            BinaryFormatter formatter = new BinaryFormatter();
            receiverList = (List<Receiver>)formatter.Deserialize(fs);
        }

Bisher wurde, zum Nutzen der Übersicht, kein Exception-Handling eingebaut, dies sollte man aber unbedingt nachholen. Gerade beim Lesen und Schreiben von Dateien kommt es immer mal wieder zu Ausnahmen (bspw. UnauthorizedAccessException)

[WP7] Der erste Tag/Systemvoraussetzungen

Da ich gerade an einer App für das mobile Betriebssystem Windows Phone 7(genauer 7.8) arbeite, dachte ich, dass ich meine ersten Eindrücke und Erfahrungen bei der Entwicklung dieser Applikation schildere.

Zum Entwicklung benötigen wir zuallererst Windows Phone SDK 7.1 oder 8 und zum Entwickeln für Windows Phone 7.8 das SDK Update[560MB,English](dieses ist mit beiden Versionen des SDK kompatibel).

Ich persönlich verwende SDK 8, da ich in Zukunft auch für Windows Phone in der Version 8 programmieren möchte.

Systemvoraussetzungen für SDK:

  • Windows 8 x64 (die Zeiten von 32-bit neigen sich wohl dem Ende)
  • 6,5 GB Festplattenspeicher
  • 4 GB Ram, wobei ich meiner VM nur 3 gegeben habe und bisher keine Probleme sehe

Zusätzliche Systemvoraussetzungen für den Emulator:

  • Windows 8 Pro oder höher
  • Prozessor mit SLAT(Second Level Address Translation)
    • Prozessoren, welche SLAT unterstützen:
    • Intel(hier EPT genannt): Intel Core i3, i5, i7 ab Nehalem(komplette Liste)
    • AMD(hier RVI genannt): Prozessoren ab der dritten Generation von Opteron(Prozessorenliste)

Da ich „nur“ einen Intel C2Q Q6600 in meinem Prozessor verbaut habe, musste ich leider auf den Emulator verzichten(da ich ein fürs Entwickeln freigeschaltenes Phone besitze, war das aber kein Problem)

© 2019 Abou Chleih. Alle Rechte vorbehalten.

Thema von Anders Norén.