Im Rahmen eines Projektes wollte ich ursprünglich Strings mittels eines asymmetrischen Verschlüsselungsverfahrens verschlüsseln. Hierzu wählte ich als Verfahren RSA. Nachdem ich alles soweit fertig hatte, ist mir leider aufgefallen, dass RSA mittels des privaten Schlüssels nur entschlüsseln kann, nicht jedoch verschlüsseln. Um trotzdem Nutzen aus der Sache zu ziehen (es ging nur um Prüfung von Daten), nutze ich den privaten Schlüssel nun zur Erzeugung einer Datensignatur. Die Anwendung des Anwenders kann diese mittels des öffentlichen Keys problemlos überprüfen.
Da es recht aufwendig ist, die geeigneten konkreten Klassen und Funktionen zur Erzeugung und Speichern der Schlüssel sowie zur Anwendung der Signierung herauszusuchen, habe ich eine kleine Demo-Anwendung geschrieben. Die jeweils benötigten Eingabefelder wurden farbig passend hervorgehoben.
Benötigt wird zuerst der passende Namespace:
using System.Security.Cryptography;
Die Erzeugung der Schlüssel ist extrem einfach:
var rsa = new RSACryptoServiceProvider();
textBoxPrivateKey.Text = rsa.ToXmlString(true);
textBoxPublicKey.Text = rsa.ToXmlString(false);
Die ToXmlString-Funktion erzeugt ein sehr gut speicherbares Format der Schlüssel, dass so auch wieder gut eingelesen werden kann. Wichtig ist aber, die Signatur in ein übertragungstaugliches Format zu bringen, da diese standardmäßig aus Bytes besteht. Hierzu nutze ich eine Hilfsfunktion BytesToHex:
public static string BytesToHex(byte[] bytes)
{
var hexString = new StringBuilder(bytes.Length);
for (int i = 0; i < bytes.Length; i++)
{
hexString.Append(bytes[i].ToString("X2"));
}
return hexString.ToString();
}
private void ButtonSignTextClick(object sender, EventArgs e)
{
var rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(textBoxPrivateKey.Text);
var encryptedSymmetricKey = rsa.SignData(
Encoding.Unicode.GetBytes(textBoxSourceText.Text),
new SHA1CryptoServiceProvider());
textBoxSignature.Text = BytesToHex(encryptedSymmetricKey);
}
Ist die Signatur erstellt, kann sie auf ähnliches Weg überprüft werden.
public static byte[] StringToByteArray(String hex)
{
int numberChars = hex.Length;
var bytes = new byte[numberChars / 2];
for (int i = 0; i < numberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
private void ButtonTestSignClick(object sender, EventArgs e)
{
var rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(textBoxPublicKey.Text);
byte[] signText = StringToByteArray(textBoxSignature.Text);
byte[] originalText = Encoding.Unicode.GetBytes(textBoxSourceText.Text);
bool signOk = rsa.VerifyData(originalText, new SHA1CryptoServiceProvider(), signText);
MessageBox.Show("Signature OK: " + signOk);
}
Hier kann das ganze noch als VS2008-Anwendung mit einer ausführbaren Datei zusammen heruntergeladen werden: RSA Source
In letzter Zeit findet man immer wieder Berichte, wie man Online-Casinos angeblich mit einem todsicherem System austricksen könnte. Das soll wie folgt funktionieren:
Man setzt einen Euro auf eine Farbe. Gewinnt man, fährt man mit einem Euro fort. Verliert man, so setzt man das doppelte und zwar solange, bis man wieder gewinnt. Der Verlust wird dadurch vollständig ausgeglichen und ist auch unter dem Begriff Martingal Roulette System bekannt.

Das klingt erst mal gut, funktioniert auch theoretisch. Allerdings gibt es ein paar praktische Probleme: Tischlimits führen schnell dazu, dass man seine Strategie nicht fortsetzen kann. Der Gewinn pro Runde bleibt bei einem Euro stehen. Man muss also 200 erfolgreiche Runden absolvieren, für einen Gewinn von 200 Euro…
Ich habe dazu eine kleine Anwendung geschrieben, die das ganze durchtestet. Die einzelnen Parameter sind einstellbar, ob man gewinnt oder nicht, wird dem Zufall überlassen. Die Anwendungen läuft, bis entweder die maximale Rundenzahl erreicht wurde, oder man den Einsatz mangels Kapital nicht mehr bringen kann. Das geht oft schneller, als gewünscht. Am Ende siegt eben immer noch die Bank:
Downloads:
Ausführbare Anwendung (10 kByte), Microsoft .Net 2.0 erforderlich
Quellcode für C# 2.0 (18 kByte)
Mit diesem Beitrag möchte ich mich der Blog-Parade zum Thema der beliebtesten Fachbücher aus dem NET-Bereich anschließen.
1. Richtlinien für das Framework-Design
Dieses Buch enthält Richtlinien für die Namensgebung im .NET-Bereich. Außerdem enthält man Einblicke, wie speziell die Konventionen innerhalb des .NET-Frameworks herausgearbeitet wurden. Nach dem Durcharbeiten des Buches fällt es leichter, Namensfehler zu vermeiden und die Benennung geschickter zu wählen.
2. C# 3.0 Entwurfsmuster
Das Buch ist eine Einführung in Entwurfsmuster. Vielen andere Einführungen, welche nicht speziell auf die Spracheigenschaften von C# ausgelegt sind, wirken komisch, wenn am Ende des Kapitels beispielsweise klar wird, welche bekannte C#-Technik dahinter steckt.
3. Grundlagen der Anwendungsentwicklung mit dem .NET Framework 2.0
Dieses Buch setzt vernünftiges Grundwissen voraus und vermittelt einen darauf basierenden breiten Querschnitt über die vorhandenen Technologien und deren Anwendung. Es zeigt das .NET Framework 2.0 aus der Sicht, wie es von Microsoft konzipiert wurde und wie es eingesetzt werden sollte.
Seit geraumer Zeit zeigte die Code-Vervollständigung unter C# folgendes Bild:

Dabei sind viele unnütze if-Kombination zu sehen, welche sich auf vorhandene Namespace-Definitionen beziehen. Nach längerer Suche konnte ich den Verursacher ausmachen. Ich verwende in den betroffenen Projekten VistaDB. In der Version 3.5 build 84 wurde die Library mittels Obfuscation unleserlich gemacht. Leider hat genau diese Verschlüsselung der Bibliothek den Effekt, dass jedes nicht öffentliche Objekt in einen eigenen neuen Namespace gelegt wird, hier also beispielsweise:
if
If
IF
@if
Diese Namespace-Definitionen bietet der Resharper per Code-Vervollständigung. Leider steht sowohl seitens VistaDB als auch seitens JetBrains eine Lösung noch aus.
Möchte man Listen (System.Collection.Generic.List<>) an Comboboxen mittels einer Master-Detail-Beziehung anbinden, so bietet sich die hier dargestellte Vorgehensweise an.
Code der Listen:
public class Detail
{
public String Caption { get; set; }
public int Id { get; set; }
public Detail(string caption)
{
Caption = caption;
}
}
public class DetailList: List<Detail>
{
public String DetailListCaption { get; set; }
public DetailList(string masterCaption)
{
DetailListCaption = masterCaption;
}
}
public class Master: List<DetailList>
{ }
Zur Anwendung wird die Liste bespielhaft befüllt:
private Master master = new Master();
...
master.Add(new DetailList("Cap Master 1"));
master.Add(new DetailList("Cap Master 2"));
master[0].Add(new Detail("Cap Detail 1.1"));
master[0].Add(new Detail("Cap Detail 1.2"));
master[1].Add(new Detail("Cap Detail 2.1"));
master[1].Add(new Detail("Cap Detail 2.2"));
Nun werden noch zwei BindingSource-Komponenten erzeugt und initialisiert:
masterBindingSource = new BindingSource {DataSource = typeof (Master)};
comboBox1.DataSource = masterBindingSource;
comboBox1.DisplayMember = "DetailListCaption";
detailBindingSource = new BindingSource {DataSource = typeof (DetailList)};
comboBox2.DataSource = detailBindingSource;
comboBox2.DisplayMember = "Caption";
masterBindingSource.CurrentChanged += masterBindingSource_CurrentChanged;
...
private void masterBindingSource_CurrentChanged(object sender, EventArgs e)
{
detailBindingSource.DataSource = masterBindingSource.Current;
}
Wichtig ist dann nur noch die Initialisierung der Datasource mit einem konkreten Wert:
masterBindingSource.DataSource = master;
Es gibt in Microsoft .NET alle gebräuchlichen Möglichkeiten, Zahlen zu runden:
using System;
namespace RoundTest
{
class Program
{
static void TestOutput(double value)
{
Console.WriteLine();
Console.Write(value);
Console.Write("\t Truncate: " + Convert.ToInt32(Math.Truncate(value)));
Console.Write("\t Ceiling : " + Convert.ToInt32(Math.Ceiling(value)));
Console.Write("\t RoundAFZ: " + Convert.ToInt32(Math.Round(value, MidpointRounding.AwayFromZero)));
Console.Write("\t RoundToE: " + Convert.ToInt32(Math.Round(value, MidpointRounding.ToEven)));
}
static void Main(string[] args)
{
TestOutput(10.0);
// ...
TestOutput(-10.51);
Console.ReadLine();
}
}
}
Hier ist die Ergebnisausgabe davon zu finden:
10 Truncate: 10 Ceiling : 10 RoundAFZ: 10 RoundToE: 10
10,49 Truncate: 10 Ceiling : 11 RoundAFZ: 10 RoundToE: 10
10,5 Truncate: 10 Ceiling : 11 RoundAFZ: 11 RoundToE: 10
10,51 Truncate: 10 Ceiling : 11 RoundAFZ: 11 RoundToE: 11
-10 Truncate: -10 Ceiling : -10 RoundAFZ: -10 RoundToE: -10
-10,49 Truncate: -10 Ceiling : -10 RoundAFZ: -10 RoundToE: -10
-10,5 Truncate: -10 Ceiling : -10 RoundAFZ: -11 RoundToE: -10
-10,51 Truncate: -10 Ceiling : -10 RoundAFZ: -11 RoundToE: -11

Möchte man Extensions in C# verwenden, scheint man auf das 3er Net-Framework angewiesen zu sein. Dies ist aber nicht ganz richtig. Legt man folgende Klasse in dem Projekt an:
namespace System.Runtime.CompilerServices
{
public class ExtensionAttribute : Attribute {}
}
so kann man Extensions, eine 3er Compiler vorrausgesetzt (VS2008), problemlos mit dem .NET Framework 2 als Ziel verwenden.
Bereits mit der Version 2.0 wurde C# um ein mächtiges Feature erweitert: yield return.
Mit C# 3.0 kam dann unter anderem das var dazu. Dazu ein kleines Beispiel:
private static void TestList()
{
var list = new List<String> {
"Dies ist Zeile 1",
"HIER KOMMT ZEILE 2",
"3 hat nichts",
"4 IST NOCH MAL EIN TREFFER" };
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.WriteLine();
foreach (var item in FilterUpperLines(list))
{
Console.WriteLine(item);
}
}
private static IEnumerable<String> FilterUpperLines(List<String> list)
{
foreach (string elem in list)
{
if (elem.ToUpper() == elem)
yield return elem;
}
}
var kann bei einer Variablendeklaration anstelle des Typs gesetzt werden, sofern die Definition für den Compiler zwingend ist. Das macht meiner Meinung nach immer dann Sinn, wenn der Typ beispielsweise durch die Initialisierung direkt dahinter nochmals wiederholt wird.
yield kann einem sehr viel Arbeit abnehmen, wenn man Mengen zurückgeben möchte. yield return sammelt quasi die Ergebnismenge ein und gibt sie in Form einer Liste zurück. Das eigentliche Definieren und zurückgeben der Liste übernimmt komplett der Compiler.
Der Clou am var ist übrigens: Diese Funktionen lassen sich auch bei einem Compilat für das Net-Framework 2.0 nutzen! Der Compiler braucht für die Funktionalität keine Features aus dem 3er Framework.
Seit dem Wechsel von Delphi auf C# habe die sprechende Art, mit Aufzählungen zu arbeiten, vermisst:
type
Right = (rRead, rWrite, rDelete);
Rights = set of Right;
var rightSet: Rights;
begin
rightSet := [rRead, rWrite];
Writeln('Schreibrecht: ' + BoolToStr(rWrite in rightSet));
rightSet := rightSet - [rWrite];
Writeln('Schreibrecht: ' + BoolToStr(rWrite in rightSet));
Readln;
end.
Das gleiche in C# war durchaus effektiv und klar, aber die boolschen Operationen lassen sich oft nicht so intuitiv lesen bzw. umsetzen:
[Flags]
enum Rights
{
Read = 1,
Write = 2,
Delete = 4,
}
private static void BoolscherCode()
{
// üblicher Code
Rights rights = Rights.Read | Rights.Write;
// prüfen, ob Schreiben enthalten ist:
Console.WriteLine("Schreibrecht: " + ((rights & Rights.Write) == Rights.Write).ToString());
// Schreibrecht entfernen
rights &= ~Rights.Write;
// prüfen, ob Schreibrecht enthalten ist:
Console.WriteLine("Schreibrecht: " + ((rights & Rights.Write) == Rights.Write).ToString());
}
Wintellect bietet mit seiner PowerCollection hier durchaus einen Ausweg:
enum Rights { Read, Write, Delete }
private static void ListenCode()
{
Wintellect.PowerCollections.Set rights = new Wintellect.PowerCollections.Set();
rights.Add(Rights.Read);
rights.Add(Rights.Write);
// prüfen, ob Schreiben enthalten ist:
Console.WriteLine("Schreibrecht: " + (rights.Contains(Rights.Write)).ToString());
// Schreibrecht entfernen
rights.Remove(Rights.Write);
// prüfen, ob Schreibrecht enthalten ist:
Console.WriteLine("Schreibrecht: " + (rights.Contains(Rights.Write)).ToString());
}
ConnectionStrings werden von Visual Studio automatisch in die Anwendungseinstellungen übernommen und beinhalten bei lokalen Datenbanken (beispielsweise VistaDB) üblicherweise auch den absoluten Pfad. Für Asp.Net bietet sich wohl eine solche Lösung an:
Data Source=|DataDirectory|\MyDatabase.vdb3
Das funktioniert aber nicht bei WinForm-Anwendungen, weil es das DataDirectory dort nicht gibt. In dem Falle kann man den ConnectionString aber für die Entwicklungsumgebung fix setzen und dann einfach zur Laufzeit korrigieren:
string connString = global::MyLib.Properties.Settings.Default.ConnectionStringMyApp;
string dbName = "MyDatabase.vdb3";
Int32 startPos = connString.IndexOf("Data Source =\"") + 13;
Int32 endPos = connString.IndexOf(dbName + "\"") + dbName.Length;
connString = connString.Substring(0, startPos + 1) +
"D:\\MyDataDir\\" + dbName + connString.Substring(endPos);
global::MyLib.Properties.Settings.Default["ConnectionStringMyApp"] = connString;
Das funktioniert, wenn man weiß, dass die Properties in den Settings problemlos über Default[] beschrieben werden können.