Artikel

Oktober 2003 | Artikel

Schlüsseldienst

(Link zum Artikel: http://www.it-republik.de/dotnet/artikel/0445)

Kryptographie in .NET - Teil 2: Sicherung von Daten und Kommunikation

Text: von Tobias Grasl
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share
Das rasante Wachstum digitaler Kommunikation bringt auch erhöhte Anforderungen an die zugrunde liegenden Protokolle mit sich. Während Sicherheit bei den meisten Webseiten und eMails keine große Rolle spielt, fordert der elektronische Austausch von Verträgen oder Zahlungsmitteln, dass die Herkunft der Daten verifiziert und im nachhinein nicht geleugnet werden. In diesem Artikel zeige ich Ihnen, wie man mithilfe asymmetrischer Algorithmen und geeigneter Kryptographischer Protokolle diesen Anforderungen gerecht wird.

In meinem letzten Artikel habe ich Ihnen gezeigt, wie man symmetrische Algorithmen und Hash-Algorithmen verwendet, um Daten zu verschlüsseln und ihre Integrität sicherzustellen. Daneben habe ich auch die Klasse RandomNumberGenerator vorgestellt, mit der Sie kryptographisch sichere Zufallswerte erzeugen können. Neben diesen Anwendungsgebieten gibt es aber noch zwei weitere, die vor allem bei der Kommunikation und dem Austausch von Daten wichtig sind. Zur Erinnerung:

  • Authentisierung: Wenn Sie im Restaurant mit ihrer Kreditkarte bezahlen, dann beweisen Sie durch ihre Unterschrift, dass Sie wirklich die auf der Karte genannte Person sind. Auch beim digitalen Austausch von Dokumenten ist es oft wichtig, die Identität des Kommunikationspartners zu verifizieren. Diese Aufgabe wird zumeist durch digitale Signaturen und Zertifikate gelöst.
  • Nachweisbarkeit: Nachdem ich einen Vertrag für ein neues Auto unterschrieben habe, ist dies für den Verkäufer relativ einfach nachzuweisen - meine Unterschrift genügt. Beim elektronischen Austausch von Dokumenten wird das gleiche mithilfe von Kryptographie und digitalen Signaturen erreicht. Diese Funktion geht zumeist mit der Authentisierung einher - da die Signatur meine Identität bestätigt, macht sie es mir im Nachhinein auch schwer, die Herkunft der Daten zu leugnen.
Diese Aufgaben werden zumeist mithilfe asymmetrischer Algorithmen erreicht und diese werde ich Ihnen in diesem Artikel vorstellen. Daneben zeige ich Ihnen, wie sie alle vorgestellten Methoden gemeinsam zu einem Kryptographischen Protokoll verbinden können, um ein insgesamt sicheres System zu erstellen.
Public Key Kryptographie
Sie werden sich vielleicht fragen, wie man mit einem öffentlichen Schlüssel (public key) Kryptographie betreiben kann - schließlich nützt es Ihnen nichts, wenn jeder Ihre Nachricht entschlüsseln kann!

Des Rätsels Auflösung liegt im Wort asymmetrisch, denn es bedeutet einfach, dass es zwei verschiedene Schlüssel gibt: einen für die Verschlüsselung (den Public Key) und einen für die Entschlüsselung (den Private Key). Während symmetrische Algorithmen wie ein Safe agieren - um Daten auszutauschen müssen beide Parteien Zugang zum Safe, also den selben Schlüssel haben - ähneln asymmetrische Algorithmen eher einem Postfach - jeder kann mithilfe öffentlicher Informationen, Ihrer Adresse, Daten ablegen, aber nur Sie können mit Ihrem Schlüssel die Daten wieder herausholen.
Grundlagen
Diese Art der Kryptographie wurde erstmals 1976 von Whitfield Diffie und Martin Hellman beschrieben, obwohl die NSA behauptet, das Verfahren schon ein Jahrzehnt früher entdeckt zu haben. Mathematisch basieren Algorithmen dieser Art auf so genannten Trap-Door One-Way Functions. Anders als normale Einwegfunktionen, wie zum Beispiel jene, die bei Hash-Algorithmen verwendet werden, gibt es bei diesen Funktionen ein Geheimnis (Trap-Door oder Falltür), mit deren Hilfe man den Ausgangswert berechnen kann. Dieses Geheimnis ist in der Kryptographie der private Schlüssel.

Um einen asymmetrischen Algorithmus zu verwenden, brauchen Sie ein Schlüsselpaar: Ihren privaten Schlüssel S und den öffentlichen Schlüssel S', der zumeist aus S berechnet werden kann. Der Algorithmus stellt dann eine Funktion V zur Verschlüsselung und eine Funktion E zur Entschlüsselung zur Verfügung. Wenn sie nun von Ihrer Kollegin eine verschlüsselte Nachricht M erhalten wollen, schicken Sie ihr S', woraufhin sie Ihnen C = VS'(M) zurückschickt. Da nur Sie im Besitz des privaten Schlüssels S sind, können nur Sie die Nachricht mit M = ES(C) entschlüsseln. Der Unterschied zu symmetrischen Algorithmen ist klein aber fein: Bei symmetrischen Algorithmen gab es nur einen Schlüssel, den Sie geheim halten müssen und den Sie daher Ihrer Kollegin nicht einfach mailen können. Die asymmetrische Variante erlaubt Ihnen, den öffentlichen Schlüssel S über unverschlüsselte Kanäle zu verteilen, womit die Kommunikation viel einfacher wird.

Sie fragen sich sicher, wieso man überhaut noch symmetrische Algorithmen benötigt. Der Grund ist einfach: Asymmetrische Algorithmen haben neben den genannten Vorteilen auch zwei große Nachteile. Einerseits ist die Verarbeitungsgeschwindigkeit dieser Algorithmen viel niedriger - RSA verschlüsselt Daten ungefähr 1.000 Mal langsamer als DES [1]. Andererseits sind diese Algorithmen gegen eine so genannte Chosen Plain Text Attack anfällig, da ein Angreifer ja den öffentlichen Schlüssel besitzt. Er kann also beliebige Texte verschlüsseln und diese mit der verschlüsselten Nachricht vergleichen, bis er die richtige findet. Aus diesem Grund setzt man im Allgemeinen asymmetrische Algorithmen nicht zum Verschlüsseln großer Datenmengen ein, sondern verschlüsselt diese Daten mit einem symmetrischen Algorithmus und einem zufällig gewählten Schlüssel, dem Session Key. Mit dem asymmetrischen Algorithmus wird dann nur der Session Key verschlüsselt. Mehr dazu im Abschnitt Kryptographische Protokolle.
Digitale Signaturen
Neben der Verschlüsselung bieten asymmetrische Algorithmen aber noch völlig neue Anwendungen: Digitale Signaturen. Das Verfahren ist im Prinzip genau umgekehrt wie jenes, das zur Verschlüsselung verwendet wird: Hier gibt es eine Funktion D, um eine Nachricht zu signieren und eine Funktion P, um eine Signatur zu verifizieren. Um eine signierte Nachricht M an Ihre Kollegin zu schicken, berechnen Sie mit Ihrem privaten Schlüssel C = DS(M) und mailen ihr C. Daraufhin kann sie durch M = PS'(C) verifizieren, dass die Nachricht tatsächlich mit S unterschrieben wurde und daher von Ihnen stammen muss. Somit hat sie die Nachricht authentisiert, macht es Ihnen aber auch schwer, die Nachricht zu leugnen, denn C beweist, dass M von Ihnen stammen muss.

Da auch das Signieren von Daten eine langsame Angelegenheit ist, wird meist nicht die ganze Nachricht signiert, sondern nur der Hashwert der Daten (siehe Teil 1). Da es fast unmöglich ist, zwei Nachrichten mit dem selben Hash zu finden, kann diese Signatur als Unterschrift der ganzen Nachricht akzeptiert werden. Somit erreichen Sie mit asymmetrischen Algorithmen die zwei Ziele dieses Artikels: Authentisierung und Nachweisbarkeit. Ein Algorithmus alleine reicht aber nicht aus, um ein sicheres System zu erzeugen. Wie kann ihre Kollegin sicher sein, dass S' tatsächlich Ihr öffentlicher Schlüssel ist? Was passiert, wenn Sie ihren privaten Schlüssel aus Versehen' veröffentlichen, und dann den unterschriebenen Vertrag leugnen wollen? Diese Probleme werden durch Kryptographische Protokolle gelöst. Nachdem ich die von .NET zur Verfügung gestellten asymmetrischen Algorithmen und deren Anwendung vorgestellt habe, werden wir uns diesem Thema zuwenden.
AsymmetricAlgorithm
Ähnlich wie bei den symmetrischen- und Hash-Algorithmen, die im ersten Teil des Artikels vorgestellt wurden, gibt es auch bei asymmetrischen Algorithmen eine in drei Ebenen geteilte Klassenhierarchie (siehe Abb. 1).
AsymmetricAlgorithm ist eine abstrakte Klasse, von der alle anderen Klassen ableiten. .NET stellt zwei verschiedene asymmetrische Algorithmen zur Verfügung:
  • RSA: Dieser Algorithmus ist mit Sicherheit der bekannteste asymmetrische Algorithmus, da er auch sehr einfach zu verstehen und zu implementieren ist. Er wurde 1977 vom Trio Ronald L. Rivest, Adi Shamir und Leonard Adleman entwickelt und wurde von RSA Inc. in den USA patentiert. Dieses Patent lief allerdings am 20. September 2000 ab, womit der Algorithmus nun weltweit lizenzfrei eingesetzt werden kann. Die abstrakte Klasse RSA findet bei .NET in der Klasse RSACryptoServiceProvider eine Implementierung, der das Windows CryptoAPI zugrunde liegt. RSA kann sowohl zur Verschlüsselung als auch zum Signieren von Daten eingesetzt werden.
  • DAS: Die US-Behörde NIST (National Institute of Standards and Technology) veröffentlichte im August 1991 den Algorithmus Digital Signature Algorithm (DSA) als Teil des geplanten Digital Signature Standards (DSS). Der Standard war heftig umstritten, da viele große Unternehmen bereits in RSA investiert hatten, der weltweit als De-facto-Standard galt, und auch RSA Inc. wollte natürlich keinen lizenfreien konkurrierenden Algorithmus als Standard akzeptieren. Daneben wurden auch einige Patentverletzungen beanstandet. Trotz dieser Kritik wurde der Standard 1994 erlassen. Auch hier stellt .NET eine CryptoAPI-basierte Lösung zur Verfügung, nämlich die Klasse DSACryptoServiceProvider. Wie der Name andeutet, kann dieser Algorihmus nur zur digitalen Signatur von Daten verwende werden, nicht zum Verschlüsseln.
>>KASTEN ANFANG<< Der RSA Algorithmus Der mathematische Grundlage des RSA-Algorithmus beruht auf der Tatsache, dass es schwierig ist, die Primfaktoren großer Zahlen zu berechnen. Um die Schlüssel für den Algorithmus zu berechnen, benötigt man zuerst zwei große, per Zufall berechnete Primzahlen p und q. Aus diesen Zahlen wird das Produkt n = pq berechnet. Dies ist der Modulus. Der öffentliche Schlüssel setzt sich aus n und der zufällig gewählten Zahl e (Exponent) zusammen, sodass e keine gemeinsamen Faktoren mit (p-1)(q-1) hat. Der private Schlüssel d kann dann mit dem Euklidischen Algorithmus berechnet werden, sodass
  1. ed º 1 mod (p - 1)(q - 1)
gilt. Dadurch teilen auch d und n keine Faktoren. p und q sollten nach diesen Berechnungen verworfen und niemals preisgegeben werden. Um eine Nachricht M zu verschlüsseln, teilt man diese zunächst in numerische Blöcke mi, die kleiner sind als n. Wenn p und q jeweils 100 bit lang sind, so ist n fast 200 bit lang und daher sollten die Blöcke auch etwas kleiner als 200 bit sein. Die verschlüsselte Nachricht setzt sich dann aus den Blöcken
zusammen. Um die Nachricht zu entziffern, setzt der Empfänger
ein, da
gilt. Es ist leicht zu erkennen, dass e und d in diesen Berechnungen austauschbar sind. Somit kann e zur Verschlüsselung und d zum Signieren eingesetzt werden. Die Sicherheit des Algorithmus liegt darin, dass es bislang kein bekanntes Verfahren gibt, um aus n die Faktoren p und q schnell zu berechnen. Sollte ein solches Verfahren entdeckt werden, wäre auch RSA nicht mehr sicher, denn jeder könnte aus dem Modulus n = pq die Faktoren p und q berechnen, um dann aus e, q und q den privaten Schlüssel d zu berechnen. >>KASTEN ENDE<<

Der Einsatz von asymmetrischen Algorithmen gestaltet sich bei .NET ganz anders als jener von symmetrischen. Während die Oberklasse SymmetricAlgorithm mit CreateEncryptor und CreateDecryptor algorithmusunabhängiges Programmieren ermöglicht, gibt es bei AsymmetricAlgorithm keinen einheitlichen Zugriff auf die eigentlichen Methoden. Das hängt teils damit zusammen, dass sich die verschiedenen asymmetrischen Algorithmen im Funktionsumfang stark unterscheiden. Wie sie dem Kasten Der RSA Algorithmus entnehmen können, setzt sich ein Schlüssel für diesen Algorithmus aus mehreren Werten zusammen. Dies ist bei asymmetrischen Algorithmen häufig der Fall. Der Schlüssel kann also nicht mehr als einfacher byte[] gesetzt oder gelesen werden, wie das bei SymmetricAlgorithm der Fall war. Bei AsymmetricAlgorithm erreicht man die gleiche Aufgabe mit ToXmlString und FromXmlString. Damit können Sie alle relevanten Informationen über den Schlüssel exportieren oder importieren. Die Funktion ToXmlString hat einen bool-Parameter, der angibt, ob nur der öffentliche Schlüssel exportiert werden soll oder beide Teile des Schlüssels. In vielen Fällen werden Sie ohnehin nur den öffentlichen Schlüssel Ihres Kommunikationspartners haben. Das kurze Programm
  1. RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
  2. String publicKey = rsa.ToXmlString(false);
produziert den folgenden XML-String für den öffentlichen Schlüssel:
  1. <RSAKeyValue>
  2. <Modulus>
  3. pZdZOSgDstXwMw065Ge1xF90lJWocJn/+3I8K1xLfcbEFVhG2PTc4h8rRmPU0eEx+I52/ggBa8GEYuZhilpvP4dym4kgL5ljCgRuYoJLiWIXXK4HzUz6SY+MhkX7qT/Xyq8C5oCMqD2SxyX0dobzN4CSutUeiJ79iDwFXQVgjYM=</Modulus>
  4. <Exponent>
  5. AQAB
  6. </Exponent>
  7. </RSAKeyValue>
Der Modulus des Schlüssels ist in diesem Fall 1024 bit lang, da das der Default ist.

Ich zeige Ihnen in diesem Artikel den Einsatz von RSA. Der Einsatz des DAS-Algorithmus verläuft ähnlich wie das Signieren mit RSA.
RSA
Schlüsselinformationen in XML-Form sind zum Austausch zwischen verschiedenen Systemen sehr nützlich, aber wenn Sie innerhalb eines Programms die Schlüssel verarbeiten wollen, ist XML nicht ideal. Die Klasse RSA bietet für diesen Fall die Funktionen exportParameters und ImportParameters, mit denen ein RSAParameters-Objekt gelesen oder gesetzt werden kann. Auch ExportParameters hat den gleichen bool Parameter wie schon ToXmlString. Daneben definiert RSA zwei abstrakte Funktionen: EncryptValue und DecryptValue. Beide akzeptieren einen byte[] und liefern den entsprechend verarbeiteten byte[] zurück. Sie werden diese Funktionen allerdings nie aufrufen: Die zu verschlüsselnden Daten müssen bei RSA mit einem sachgerechten Algorithmus gepolstert werden, um die größte Sicherheit zu gewährleisten. Aus diesem Grund werfen die Implementierungen dieser Funktionen in der Klasse RSACryptoServiceProvider eine NotSupportedException.
RSACryptoServiceProvider
Die Klasse RSACryptoServiceProvider implementiert den RSA-Algorithmus über das Windows CryptoAPI und stellt Ihnen auch Funktionen zum Verschlüsseln und Signieren von Daten zur Verfügung. Wie bereits erwähnt, werden meist nur ein Session Key verschlüsselt und ein Hashwert der Daten signiert. RSACryptoServiceProvider bietet daher Encrypt und Decrypt, die einen byte[] entsprechend ver- oder entschlüsseln. Beide Funktionen nehmen neben den Daten noch einen zweiten Parameter, bool fOAEP. Dieser gibt an, ob die Daten mit dem OAEP (true) oder mit dem PKCS#1 v1.5 Algorithmus aufgepolstert werden sollen.

OAEP (Optimal Asymmetric Encryption Padding) ist ein von Mihir Bellare and Phil Rogaway entwickelter Algorithmus, der die Daten so kodiert, dass ein Angreifer die verschlüsselte Nachricht nur entziffern kann, wenn er in der Lage ist, den RSA-Algorithmus zu brechen. Somit wird die Sicherheit der verschlüsselten Nachricht erhöht. OAEP ist bei manchen Windows-Versionen (z.B. W2K) nur durch Installation des High Encryption Pack verfügbar. Der Versuch, es einzusetzen, führt ansonsten zu einer Exception. Dieses Paket erhalten Sie kostenlos von der Microsoft-Website. Um Daten zu signieren, verwendet Sie entweder SignData, um Daten in einem byte[] oder InputStream zu signieren, oder SignHash, um den bereits errechneten Hashwert zu verarbeiten. Bei letzterer Funktion muss der OID (Object Identifier) des verwendeten Hash-Algorithmus als String übergeben werden, wobei nur SHA1 oder MD5 verwendet werden können. Diesen Wert erhalten Sie durch den Aufruf CryptoConfig.MapNameToOID(algorithmusName). Bei SignData können Sie hingegen den Algorithmus als Namen, Objekt des Typs HashAlgorithm oder als System.Type-Objekt übergeben.
CryptorAsym
Unser erstes Beispiel, CryptorAsym, ist eine Weiterentwicklung des CryptorPWHash-Beispiels aus Teil 1. Zwei Kollegen, Albert und Berta, wollen empfindliche Daten austauschen und müssen diese daher sowohl verschlüsseln als auch deren Herkunft verifizieren. Dazu wollen sie RSA einsetzen.

Der erste Parameter, der an das Programm übergeben wird, ist der Name des Kommunikationspartners. CryptorAsym erwartet im lokalen Verzeichnis zwei Dateien: privateKey.xml, die den eigenen RSA-Privatschlüssel enthält, und Key.xml, die den öffentlichen Schlüssel der Partei mit dem angegebenen Namen enthält. Diese zwei Dateien werden gelesen und jeweils mit
  1. rsa.FromXmlString(keyString);
  2. rsaRecipient.FromXmlString(keyString)
gesetzt. Als nächsten Schritt schreiben wir beim Verschlüsseln den Session Key, den wir mit dem RSA-Schlüssel des Kommunikationspartners verschlüsselt haben, in die Zieldatei:
  1. byte[] encryptedKey = rsaRecipient.Encrypt(rijndael.Key, false);
  2. fileOutputStream.Write(encryptedKey,0,128);
Der Empfänger muss hingegen den Schlüssel aus der Datei lesen, mit seinem privaten Schlüssel entschlüsseln und in den Rijndael-Algorithmus setzen:
  1. byte[] encryptedKey = new byte[128];
  2. fileInputStream.Read(encryptedKey,0,128);
  3. rijndael.Key = rsa.Decrypt(encryptedKey,false);
Die weiteren Schritte sind ähnlich wie bei CryptorPWHash in Teil 1. Statt SHA-512 setzen wir SHA-1 ein, da ersterer von SignHash nicht unterstützt wird. Anstatt den Hashwert nur verschlüsselt in die Datei zu schreiben, signiert ihn der Absender zusätzlich:
  1. outputStream.Write(rsa.SignHash(hashAlgorithm.Hash,CryptoConfig.MapNameToOID("SHA1")),0,128);
Der Empfänger kann hingegen den signierten Hashwert lesen und sowohl die Signatur als auch die Integrität der Daten in einem Schritt verifizieren:
  1. byte[] signature = new byte[128];
  2. Array.Copy(content,content.Length-128,signature,0,128);
  3. rsaRecipient.VerifyHash(hashAlgorithm.Hash, CryptoConfig.MapNameToOID("SHA1"), signature);
Der Beispiel-Quellcode auf der CD enthält zwei Unterverzeichnisse: Albert enthält mit privateKey.xml den privaten Schlüssel von Albert und mit BertaKey.xml den öffentlichen Schlüssel von Berta. Im Verzeichniss Berta sind analoge Dateien. Beide enthalten auch sample.txt, eine einfache Textdatei mit Beispieldaten.

Um diese Datei von Albert an Berta zu schicken, rufen Sie in seinem Verzeichnis CryptorAsym Berta sample.txt ../Berta/cipher encrypt auf. Berta kann die Daten mit CryptorAsym Albert cipher message.txt decrypt entschlüsseln und die Signatur auch verifizieren. Mit diesem Beispiel haben Sie nun ein Programm, das alle erklärten Ziele der Kryptographie erreicht: Sie stellen die Daten durch Verschlüsselung sicher, können durch die Signatur sowohl die Herkunft der Daten authentizieren als auch den Nachweis dieser Herkunft liefern, und durch den Einsatz des Hash-Algorithmus ist auch die Integrität der Daten gewährleistet.
Kryptographische Protokolle
Die besten Algorithmen und längsten Schlüssel nützen nichts, wenn sie nicht korrekt eingesetzt werden. Es gibt viele Fragen und Probleme, die mit Algorithmen alleine nicht gelöst werden können. Wie können Sie sicher sein, dass der Schlüssel, den Sie empfangen haben, wirklich mein öffentlicher Schlüssel ist? Was passiert, wenn mein Schlüssel zufällig im Internet veröffentlicht wird und ich mich dann weigere, für das gekaufte Auto zu bezahlen? Diese Probleme können Sie lösen oder zumindest mindern, indem Sie ein geeignetes Kryptographisches Protokoll einsetzen. In diesem Zusammenhang ist ein Protokoll eine Reihe von Schritten, die zwei oder mehr Parteien betreffen und die in einer wohl definierten Reihenfolge abgearbeitet werden. Jedes Protokoll hat eine gewisse Aufgabe, die es lösen soll. Ein Kryptographisches Protokoll dient dazu, die verfügbaren Algorithmen durch geeignete Verfahren zu unterstützen. Dabei geht es vor allem darum, einen möglichen Angriff auf die Kommunikation zu verhindern.

Ein sehr einfaches Protokoll haben wir in unserem Beispiel schon gesehen. Es sieht folgendermaßen aus:
  • 1. Albert und Berta einigen sich auf einen asymmetrischen Algorithmus (RSA).
  • 2. Albert schickt Berta seinen öffentlichen Schlüssel, Berta schickt Albert ihren.
  • 3. Albert erzeugt einen zufälligen Session Key und verschlüsselt ihn mit Bertas öffentlichem Schlüssel.
  • 4. Albert unterschreibt den Hash der Nachricht mit seinem privaten Schlüssel und verschlüsselt die Nachricht und Signatur mit dem Session Key.
  • 5. Albert schickt Berta sowohl den verschlüsselten Session Key als auch die verschlüsselte Nachricht mit Signatur.
  • 6. Berta entschlüsselt mit ihrem privaten Schlüssel den Session Key und anschließend die Nachricht und Signatur.
  • 7. Berta verifiziert die Signatur und überzeugt sich, dass die Nachricht von Albert kommt und nicht verändert wurde.
Dieses Protokoll ist zwar sehr einfach, aber es schützt vor so genannten passiven Attacken. Dabei kann Angreifer Max zwar zuhören, aber nicht aktiv eingreifen. Er sieht zwar die ausgetauschten Daten, kann die Nachricht aber nicht entziffern, da der Session Key nur von Berta entschlüsselt werden kann. Anders sieht es dagegen bei einer aktiven Attacke aus. Hier kann Max auch eingreifen und gewisse Nachrichten durch seine eigenen ersetzen. Max empfängt im Schritt (2) die öffentlichen Schlüssel von Albert und Berta, gibt aber beiden Parteien seinen eigenen Schlüssel weiter. Jetzt kann Max sowohl die gesamte Kommunikation entschlüsseln als auch durch seine eigenen Nachrichten ersetzen, ohne dass die zwei legitimen Parteien es mitbekommen.
Digitale Zertifikate
Diese Situation vermeiden Sie durch den Einsatz einer dritten Partei, Volker, der sowohl Albert als auch Berta vertrauen. Volker kennt die öffentlichen Schlüssel beider Parteien und nachdem diese ausgetauscht worden sind, bestätigt Volker jeder Partei, dass der besagte Schlüssel tatsächlich dem gewünschten Kommunikationspartner gehört. Volker agiert also in einer ähnlichen Funktion wie ein Notar. Max kann seinen Schlüssel nicht mehr als den von Albert oder Berta ausgeben und schlägt in seinem Angriff fehl.

Es wäre natürlich recht umständlich, für jede Kommunikation einen digitalen Notar zu benötigen. Aus diesem Grund erstellte die ISO 1988 das ISO Authentication Framework, bekannt unter dem Namen X.509. Dieser Standard definiert unter anderem Zertifikate, die ähnlich einem Pass den öffentlichen Schlüssel eines Benutzers als echt ausweisen (siehe Kasten X.509 Zertifikate). Die Rolle von Volker übernehmen so genannte Certification Authorities (CA). Um an diesem Protokoll teilzunehmen, benötigen Sie ein Zertifikat, das ihnen von einer CA ausgestellt worden ist. Um den Angriff von Max zu vereiteln, fügen wir nun einen zusätzlichen Schritt ein:
  • 2a) Albert und Berta verifizieren bei ihren CAs, dass die getauschten Schlüssel authentisch sind.
Somit können Albert und Berta jetzt eifrig kommunizieren, ohne dass Max die Daten lesen oder ändern kann. Er kann nicht eingreifen, da er sich nicht als Albert oder Berta ausweisen kann.

>>KASTEN ANFANG<<
X.509 Zertifikate
Ein wichtiger Teil des ISO Authentication Framework sind X.509-Zertifikate und die Hierachie der Certification Authorities (CA). Jeder Benutzer des Systems bekommt ein solches Protokoll von einer CA ausgestellt.
Die Certification Authorities sind in einer hierarchischen Struktur angeordnet. Jede CA hat ein Zertifikat, das sowohl von der darüber liegenden als auch den darunter liegenden CAs signiert ist. Dadurch besteht von jeder CA zu jeder anderen CA ein Zertifizierungspfad, der die beiden CAs gegenseitig als echt ausweist. Wenn Albert ein Zertifikat von CA4 und Berta eines von CA7 hat, so kann Berta sich über den Pfad (CA7, CA6, CA3,CA1,CA2, CA4) davon überzeugen, dass Alberts Zertifikat authentisch ist. Albert geht den umgekehrten Weg. Dieser Vorgang sieht zwar komplex aus, Sie benutzen ihn selbst aber sehr häufig. Gesicherte Websites verwenden solche Zertifikate, um sich als authentisch auszuweisen. Wenn Sie auf eine solche Seite gehen, können Sie per Doppelklick auf das Schloss am unteren Rand des Browsers das Zertifikat dieser Site begutachten und auch die CA sehen, von der es ausgestellt wurde. Die CAs, die Internet Explorer kennt, sehen Sie in den Einstellungen des Browsers. .NET bietet auch Klassen, um X.509-Zertifikaten zu verwalten. Diese befinden sich im Namespace System.Security.Cryptography.X509Certificates. >>KASTEN ENDE<<
Schlüsselmanagement
Die größte Gefahr für jedes kryptographische System ist sicher, dass ein Angreifer einen geheimen Schlüssel in die Hand bekommt. Neben der Ausstellung von Zertifikaten sind CAs auch dafür verantwortlich, diese zu annullieren. Jedes Zertifikat wird nur auf eine gewisse Zeit ausgestellt. Sollte die Sicherheit des privaten Schlüssels aber während dieser Zeit gefährdet sein, kann dieser auch widerrufen werden. Jede CA hat eine Liste solcher ungültigen Schlüssel.

Auch das Protokoll muss erweitert werden, um solchen Fällen gerecht zu werden: Jede Nachricht wird mit einem Zeitstempel geschickt, die auch Teil der Signatur ist. Der Zeitstempel kann dann mit dem Gültigkeitszeitraum des Zertifikats und eventuell mit dem Datum der Annullierung, verglichen werden, um die Authentizität einer Nachricht festzustellen. Dabei geht es auch darum, es einer Partei schwer zu machen, im Nachhinein die Signatur zu leugnen - ist die Signatur authentisch und das Zertifikat noch gültig, dann habe ich eigentlich keine Ausrede.
Fazit
Ich habe Ihnen in diesem Artikel eine Einführung in die Themen asymmetrische Algorithmen und Kryptographische Protkolle gegeben und gezeigt, wie Sie diese Methoden mit .NET einsetzen können. Kryptographie ist ein umfangreiches Feld, und welches Protokoll wann angewandt werden sollte, hängt vom Zweck des Systems mit ab. Wenn Sie ein System kryptographisch sichern müssen, empfehle ich weitere Lektüre - vor allem das Werk von Bruce Schneier [1] behandelt Protokolle sehr ausgiebig.

Das .NET Framework bietet neben den gezeigten Klassen noch einige kryptographische Fähigkeiten, zum Beispiel die Verarbeitung von Signaturen im Format XMLDSIG (XML Digital Signature) [2] und Hilfe beim Verwalten von Schlüsseln und X.509-Zertifikaten. Mehr dazu finden Sie in [3] und in der Dokumentation des Frameworks. Tobias Grasl ist Principal Consultant bei Persistence Software, einem Anbieter von Persistenz- und Caching-Lösungen im .NET- und J2EE-Umfeld. Er ist unter tobias.grasl@persistence.com erreichbar.
  • [1] Bruce Schneier, Angewandte Kryptographie. Protokolle, Algorithmen und Sourcecode in C, Addison-Wesley, 1996
  • [2] Informationen zum XMLDSIG Standard: www.w3.org/Signature/
  • [3] Stefan Lange et al., .NET Framework Security, Addison-Wesley, 2002


Anzeige

Kommentare

zurück zum Seitenanfang