|
Antwort |
Registriert seit: 10. Dez 2003 4 Beiträge |
#1
Hallo zusammen,
Ich möchte ein kleines Programm schreiben, das Textdateien verschlüsseln kann. Nachdem ich einige Postings (hier in Forum) zu dem Thema gelesen habe (besonderes die vom Hagen), bin ich ein Stück schlauer geworden. Jedoch stehen noch einige Fragen offen. Ich versuche einfach mal das "neue Erlernte" kurz zu schildern. Als Hash und Chiper sollte Blowfish und SHA_1 verwendet werden. Hier der Encode-Vorgang als Pseudocode:
Delphi-Quellcode:
Die Datei sieht dann so aus:procedure Encode(Source, Dest: pointer; Password: Pchar); var Key: array[0..19]of Byte; (SHA1 160 Bit) Salt: array[0..19]of Byte; begin //Salt sollte mit Zufallsdaten befüllt werden (per was? delphirandom soll lt. Hagen unsicher sein) Write(Dest, Salt); Key:= SHA1_CreateHash(Salt + Password); BlowFish_Init(Key); Source:= Key + Source; //Key sollte ja anschliessend mitverschlüsselt werden BlowFish_Encrypt(Source, Dest); end;
Delphi-Quellcode:
BOM (Byte Order Mark) damit es erkennbar wird, dass es sich um eine verschl.datei handelt.
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─ // ┬─┬─┐
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─ // ┴─┴─┘ │ │ │ │ │ │ BOM │◄------------ Salt -------------------►│◄------------- Key -------------------►│◄- Daten -►│ │ │ │ │ │ 0 4 24 44 xx Der Salt liegt in unverschlüsselter Form vor. Ab Byte 24 werden Key+Daten verschlüsselt. Soweit in Ordung? Nun die Decode-Prozedur
Delphi-Quellcode:
Nun die Fragen (Vorausgesetzt ich habe die Thematik soweit richtig verstanden ?)
procedure Decode(Source, Dest: pointer; Password: Pchar);
var Salt: array[0..19]of Byte; Key: array[0..19]of Byte; TmpKey: array[0..19]of Byte; begin Read(Source, Salt); Key:= SHA1_CreateHash(Salt + Password); BlowFish_Init(Key); BlowFish_Decrypt(Source, TmpKey, sizeof(TmpKey)); if (Key <> TmpKey) then begin MessageBox('Kennwort ungültig'); exit; end; ... //rest der daten entschlüsseln end; 1. Ist es vom Verfahren her sicher? 2. Sollte man hierfür ein Byte- oder BlockChiper verwenden? 3. Sollte man beim BlockChiper Source auf 8 Byte abrunden (falls Daten < 8 Byte)? 4. Ist mir sonst irgentwas entgangen? |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#2
1. Ist es vom Verfahren her sicher?
Falls mit dem in der Datei gespeichertem Key der Hash Wert von SHA1(Salt || Passwort) gemeint ist dann ja. Denoch würde ich empfehlen diesen Key nicht vollständig zu speichern. Er dient ja nur zur Erkennung ob man das richtige Passwort eingegeben hat und als sicherer Zufallswert. 2. Sollte man hierfür ein Byte- oder BlockChiper verwenden? Ein Byte Cipher ist ein BlockCipher mit Blockgröße 8 bit Du meintest einen StreamCipher kontrahär zum BlockCipher. Nun, das hängt vom Cipher Algorithmus selber ab, und von den Kompatibilitätsanforderungen. Zb. für Windows-Programme die mit PocketPC/Palm Programmen per TCP/IP kommunizieren nutze ich gerne RC4 als StreamCipher. Denoch werden BlockCipher als sicherer eingestuft. Blowfish ist ein BlockCipher. 3. Sollte man beim BlockChiper Source auf 8 Byte abrunden (falls Daten < 8 Byte)? Dies ist nicht zwingend notwendig. Beachte das der Ciphermode nicht ECB ist, sondern zB. CBC,CTS,CTR,CFB oder ähnliche Feedback Modis. Dies ist enorm wichtig. Alle diese Modis können per "Cipher Text Stealing" jede beliebige Messagelänge 1 zu 1 verschlüsseln. Ein zusätzliches Padding ist also nicht nötig. 4. Ist mir sonst irgentwas entgangen? Keine Ahnung, das müsstest du besser wissen als ich Spaß beiseite, so wie es jetzt ist ist es schon besser als viele andere Lösungen. Ich versuche hier nochmal zusammenzufassen.
Code:
Nun, unser Salt ist zufallig und somit wird SKey = SHA1(Salt || Key)Key = Benutzerpasswort Salt = Zufallswert ca. 16 Bytes groß, kann mit Delphi Random() erzeugt werden SKey = Sessionkey der als Key für die Verschlüsselung benutzt wird wir bilden Salt := Random(); SKey := SHA1(Salt || Key); Dateiaufbau H || C Header || Salt || SKey || Message H ist unverschlüsselt C wird sequentiell verschlüsselt mit Blowfish und SKey, dabei MUSS ein FeedbackModus benutzt werden. 1.) ebenfalls pseudozufällig sein, und 2.) das Userpasswort Key vor Komprimitierung schützen C, die Nachricht, wird am Anfang mit dem SKey expandiert. Da wir einen Cipher mit Feedbackmodus benutzen wird jeder zu verschlüsselnde Block mit dem verschlüsselten Vorgängerblock verknüpft. Dies ist nun wichtig, denn unser SKey in C ist ja ebenfalls Pseudozufällig. Damit ist der 1. Block im Feedbackmodus des Cipher sozusagen zufällig basierend auf dem Salt + Key. Dieser 1. Block hat direkten Einfluß auf die verschlüsselungen jedes nachfolgenden Message-Blockes. Lesbar in der Datei stehen also Header + Zufalls Salt. Nur mit dem richtigen Passwort kann man den SKey erzeugen, der mit gleichbleibendem Userpasswort Key denoch IMMER pseudozufällig sein wird. D.h. werden die gleichen Daten mit dem selben Key mehrmals verschlüsselt so sind deren Resultate IMMER unterschiedlich, auf grund des Salts. Bei einer Entschlüsselung fragt man Key beim User ab, liest Salt aus Datei, erzeugt SKey = SHA1(Salt || Key), initialisiert Blowfish mit Passwort SKey, entschlüsselt aus C den SKey', und vergleicht diesen mit SKey = SKey'. Sollten beide gleich sein so wurde die Datei mit Key verschlüsselt. Nun kann man aus C die Message entschlüsseln. Ein Angreifer kann nun nur noch eine Brute Force Attacke durchführen. Alle anderen Attacken, wie Known Plain Text, Reply Attacks usw. sind mit hoher Wahrscheinlichkeit ausgeschlossen, da über den Zufalls-Salt sowohl das Userpasswort Key als auch die Verschlüsselung von C über dessen Expandierung mit SKey, randomisiert wurde. Wichtig dabei ist es das C in einem Rutsch mit Hilfe eines BlockCipher und einem Feedback Ciphermode, verschlüsselt wurde. Bei einer Brute Force Attacke, in der ja nach Key gesucht wird, wird er Angreifer alle möglichen Key's durchprobieren, den Salt auslesen, SKeyTest = SHA1(Salt || TestKey) berechnen und die ersten 20 Bytes aus C entschlüsseln. Sind diese 20 Bytes gleich SKeyTest, dann hat er mit hoher Wahrscheinlichkeit Key = KeyTest gefunden, und kann den Rest der Nachricht entschlüsseln. Man sieht also das das ganze Konstrukt nur die vielen möglichen Attacken verhindert, aber immer noch absolut von der Qualität vom Key abhängt. Andereseits kann man logisch erkennen das obiges Konstrukt in keinster Weise die Sicherheit der Einzelkomponenten reduziert, also schwächt. Ein Key wie "A" ist also in jedem Falle unsicher, da helfen auch keinerlei Tricks, Kniffe oder Super-Algorithmen. Um noch mehr Sicherheit zu erreichen muß man an der PKI arbeiten, sprich Schlüssel Infrastruktur. Würde man mit Public Keys arbeiten so würde man Key selber aus Zufall erzeugen und ihn verschlüsselt, durch den Public Key, in der Datei speichern. Nur wer den Privaten Schlüssel besitzt kann diesen Zufalls-Key aus der Datei entschlüsseln. Selbst wenn der Private Schlüssel durch das Passwort "A" geschützt wäre, wäre dieses System denoch sicherer. Denn, der Private Schlüssel steht nur auf Rechnern bzw. Medien zur Verfügung die nur dem berechtigten Benutzer zugänglich sind. Gruß Hagen |
Zitat |
Registriert seit: 10. Dez 2003 4 Beiträge |
#3
@Hagen
erst mal besten Dank für die schnelle und informative Antwort. 1. Byte- oder BlockChiper ... Definitionirrtum von mir, ich dachte ByteChiper sind die, die mit Buffer=1Byte arbeiten. Hier geht’s aber mehr um den Ciphermode. 2. Delphirandom diesbezüglich brauche ich mir keine weiteren Sorgen (unsicher und so) machen? Ich habe versucht das "Besprochene" Testweise (mit DEC) nachzubauen. Bin dabei auf Folgendes gestoßen, wenn man den CBC-Mode anwendet, hat man das Problem, dass der verschlüsselt gespeicherter Sessionkey falsch entschlüsselt wird. Lieg daran, das in dem Mode die letzten 4 Bytes (Sessionkey) anderes behandelt werden, weil 20 mod 8 <> 0 ist (ich kanns leider nicht besser erklären). Ich konnte mir natürlich helfen, in dem ich beim Sessionkey-Entschlüsseln einfach 4 bytes mehr mitgenommen habe. Du hast bestimmt besseren Vorschlag Im CFB-Mode tritt das Problem natürlich nicht auf, da Byte für Byte entschlüsselt wird. Ich dachte nur, dass der CBC sicherer + schneller wäre, oder? Den Test-Code lege ich bei, vielleicht (wenn’s nicht allzu viel Arbeit macht) "fliegst Du mal drüber"?
Delphi-Quellcode:
Gruß Andreasvar // "Virtuelle Datei" Aufbau in Bytes: // 0-19 : Salt // 20-39: SKey // 40-XX: Message g_File: Pointer; g_FileSize: Integer; procedure TForm1.EncodeClick(Sender: TObject); var hash: THash_SHA1; fish: TCipher_Blowfish; salt: array[0..19] of Byte; temp: Pointer; begin //Zufallsdaten erzeugen RndXORBuffer(RndTimeSeed, salt, sizeof(salt)); //Sessionskey erzeugen hash:= THash_SHA1.Create(nil); hash.Init; hash.Calc(salt, sizeof(salt)); hash.Calc(PChar('Who is John Galt?')^, 17); hash.Done; //Temp. Speicher fur SKey + Message reservieren GetMem(temp, hash.DigestKeySize + Length(Memo1.Text) + 1); //SKey erstmal nach temp kopieren, soll ja mit Message in einem Rutsch verschlüsselt werden Move(hash.DigestKey^, temp^, hash.DigestKeySize); //den Text der Nachricht hollen SendMessage(Memo1.Handle, WM_GETTEXT, Length(Memo1.Text) + 1, LongInt(PChar(temp) + hash.DigestKeySize)); //Speicher für die "virtuelle Datei", hierdrüber soll der Test-Austausch erfolgen g_FileSize:= sizeof(salt) + hash.DigestKeySize + Length(Memo1.Text); GetMem(g_File, g_FileSize + 1); //Salt unverschlüsselt in die "Datei" schreiben Move(salt, g_File^, sizeof(salt)); //den Fish erstellen und initialzieren fish:= TCipher_Blowfish.Create('', nil); fish.Mode:= cmCBC; fish.Init(hash.DigestKey^, hash.DigestKeySize, nil); //Datei (ohne salt) verschlüsseln fish.EncodeBuffer(temp^, PChar(PChar(g_File) + 20)^, g_FileSize - sizeof(salt)); //Spasseshalber den verschl. Text ausgeben Memo2.Text:= StrToFormat(pchar(g_File), g_FileSize, fmtHEX); //aufräumen sollte man auch nicht vergessen hash.Free; fish.Free; freemem(temp); end; procedure TForm1.DecodeClick(Sender: TObject); var hash: THash_SHA1; fish: TCipher_Blowfish; temp: Pointer; key2: array[0..19+4] of Byte;//4 Byte mehr wegen CBC begin //Sessionskey erzeugen hash:= THash_SHA1.Create(nil); hash.Init; hash.Calc(g_File^, 20); //den Salt-Wert nicht vergessen hash.Calc(PChar('Who is John Galt?')^, 17); hash.Done; //Chiper erstellen und initialzieren fish:= TCipher_Blowfish.Create('', nil); fish.Mode:= cmCBC; fish.Init(hash.DigestKey^, hash.DigestKeySize, nil); //verschl. SKey decodieren fish.DecodeBuffer(PChar(g_File)[20], key2, 20+4); if not CompareMem(hash.DigestKey, @key2, 20) then begin ShowMessage('Invalid Password'#13#10 + 'SKey1: ' + StrToFormat(PChar(hash.DigestKey), 20, fmtHEX) + #13#10 + 'SKey2: ' + StrToFormat(PChar(@key2), 20, fmtHEX)); end else begin GetMem(temp, g_FileSize - 20 + 1); fish.DecodeBuffer(pchar(g_File)[20], temp^, g_FileSize - 20); //den entschl. Text (ohne salt) ausgeben SendMessage(Memo3.Handle, WM_SETTEXT, 0, LongInt(@PChar(temp)[20])); FreeMem(temp); end; hash.Free; fish.Free; end; PS: Ich hoffe, das ich mit meinem miserabelschlechten Deutsch die Ohren der Forumteilnehmer nicht beleidigt habe. |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#4
Zitat:
1. Byte- oder BlockChiper ...
Definitionirrtum von mir, ich dachte ByteChiper sind die, die mit Buffer=1Byte arbeiten. Hier geht’s aber mehr um den Ciphermode. D.h. Bei StreamCiphern ist die Nachricht wie ein Strom von Informationen, die immer Byteweise abhänig voneinander verschlüsselt werden. Die Verschlüsselungsfunktion besteht darin das der StreamCipher selber einen Schlüsselstrom erzeugt und diesen mit dem Datenstrom verküpft, z.B. XOR. Der Streamcipher Algorithmus arbeitet also NIEMALS direkt mit dem eigentlichen Datenstrom. Beim Knacken versucht man also den Schlüsselstrom des Streamciphers zu finden. Ein Blockcipher zerhackt den Datenstrom in Blöcke, und benutzt nun seinen Algorithmus um jeden Block für sich gesehen, zu verschlüsseln. D.h. ein Blockcipher arbeitet direkt mit dem Datenstrom. Somit ist die Verschlüsselung jedes einzelnen Blockes eines Datenstromes unabhänig von den anderen Blöcken. (im ECB Mode) Während bei einem StreamCipher der gleiche Schlüsselstrom-Algorithmus zur Verschlüsselung und Entschlüsselung benutzt wird, werden beim Blockcipher zwei unterschiedliche Algorithmen benötigt. Ein Algorithmus zur Verschlüsselung und ein inverses Gegenteil zur Entschlüsslung. Das Passwort des Benutzers stellt nun sicher das beide Algorithmen mit den korrekten Daten auch korrekt funktionieren. D.h. das Passwort ist ein Schlüssel der die beiden unterschiedlichen Algortihmen des BlockCiphers synchronisiert, so daß der Entschlüsselungs-Algo das entschlüsseln kann was sein Gegenpart der Verschlüsselungsalgo. verschlüsselt hat. So, ich hoffe diese vereinfachte Darstellung macht nun den Hauptunterschied zwischen Stromverschlüsselungen und Blockverschlüsselungen deutlich. Man dürfte sehr einfach ableiten können was dies für die Sicherheit, eg. Stärke des Algos. zu bedeuten hat.
Zitat:
2. Delphirandom
diesbezüglich brauche ich mir keine weiteren Sorgen (unsicher und so) machen? Da wir aber SKey in C am Anfang einbetten, und der Cipher einen Feedback Modus benutzt, wird sogar noch die Verschlüsslung gestärkt. Betrachtet man in diesem Falle SKey als sicheren Zufallswert, so wird dadurch die Nachricht zufällig gemacht. Aus Sicht des Ciphers wird also eine "Zufallsnachricht" verschlüsselt. Ein Angreifer hat also keinerlei Angriffstellen mit bekannten Informationen um diese auszunutzen. Wichtig dabei ist aber das man SKey nicht ohne ein Geheimniss reproduzieren kann. Wäre SKey in C nur ein Zufallssalt durch Delphis Random() erzeugt, so wäre es wieder unsicher.
Zitat:
Ich habe versucht das "Besprochene" Testweise (mit DEC) nachzubauen....
Der nachfoldende Aufruf von .DecodeBuffer() entschlüsselt dann den Rest der Nachricht. Die konplette Nachricht wird dann zusammengesetzt aus den 4 Bytes + dem Rest. Wird dies nicht beachtet so schlägt sofort das autom. Padding = Cipher Text Stealing des Feedback Modus (CBC, CTS usw.) zu. Dieses "Padding" ist nicht-reversibel. D.h. hat der Ciphermode einmal dieses Padding durchgeführt ist der interne Status des Cipher verändert worden. Exakt diese Eigenschaft des Ciphermodes ist dir zum Verhängnis geworden. Zur Verschlüsselnungsphase hast du die komplette Nachricht nur einemal "ge-padded" am Schluß, und zur Entschlüsselungsphase aber zweimal -> einmal beim entschlüsseln der 20 Bytes von SKey und dann man Schluß der restlichen Nachricht. Deshalb bist du auf die Idee gekommen die 20 Bytes von SKey um 4 Bytes zu verlängern. Denn nun ist die Blockgröße wieder synchronisiert zum Ciphermode. Allerdings funktioniert dies dann aber nur mit Ciphern die zB. 1,2,4,8 Bytes Blockgröße haben. Ein 16 Bytes BlockCipher würde wiederum Schwierigkeiten machen. Deshalb ist es besser einfach x Blöcke der kompletten Nachricht am Anfang zu entschlüsseln. X sollte so groß sein das damit mehr als 20 Bytes entschlüsslt werden. Z.b. ein 16 Bytes Blockcipher entschlüsselt 2 Blöcke = 32 Bytes. Davon sind 20 Bytes = SKey und 12 Bytes von der Nachricht. Diese 12 bytes werden dann in den Zielstream gespeichert. Es gibt Ciphermodis die dies umgehen können, zb. Bruce Schneiers CFB64 Modus. Intern arbeitt dieser Modus immer auf der Blockgröße des Ciphers. Im Falle von Blowfish also 8Bytes = 64 Bit, deshalb CFB64. Im Falle eines 16 Bytes Blockciphers würde dieser Modus dann CFB128 heissen. Wichtig ist aber nur das dieser Modus unabhänig ist von der Nachrichtenlänge funktioniert. Man kann also den Cipher in mehreren Gängen zB, jeweils mit 2 Bytes, dann 5 Bytes dann 12 bytes füttern, ohne das beim nachfolgendem entschlüsseln mit anderen Datenentnahmegrößen es zu Problemen kommt. Gruß Hagen |
Zitat |
Registriert seit: 10. Dez 2003 4 Beiträge |
#5
Ok, ich habe den Code soweit geändert,
so dass die Entschlüsselung dort fortgesetzt wird, wo die Enschlüsselung des Skey aufgehört hat. Es ist recht viel Arbeit mit CBC-Mode. Im CFB/OFB-Mode hätte man die Probleme erst gar nicht. Was haben die Modis für Nachteile (abgesehen von Performance) ? |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#6
Eben die Performance Dafür haben sie auch Vorteile. Angenommen Blowfish im CFB8 Mode, da Blowhish mit 8 Bytes Blockgröße arbeitet wird ein Datenblock eben 8 mal im OFB verschlüsselt, jeweils um 1 Byte versetzt. Ginge man von idealistischen Sicherheitsanalysen aus so wäre der CFB8 Modus also 8 mal sicherer als CBC.
Allerdings es gibt ganz wesentliche Unterschiede zwischen CFB und OFB. OFB also Output Feedback Modus verwandelt einen BlockCipher in einen StreamCipher. Ich persönlich schätze diese Konstruktion als Abschwächung der Sicherheit ein (versch. Crypto-Gurus sehen es ebenso). Leider ist im offiziellen DEC Part I Version 3.0 noch nicht der CFBx Modus enthalten. Das x steht für die Blockgröße des Ciphers. Im grunde ist das dann ein CBC Modus der aber ohne Padding oder Cipher Text Stealing auskommt. Du kannst aber diesen Modus mit DEC nachbauen. Dazu muß der Cipher im ECB Modus initialisiert werden, und der CFBx Code in selbergeschriebenen Proceduren codiert werden. Gruß hagen |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#7
Ey das ist ja cool. Ihr habt DEC in eure Datenbank aufgenommen DAAANNNNKEEEE !
Da sollte ich mich wohl mit einem DP-DEC revanchieren. Gruß Hagen |
Zitat |
Registriert seit: 10. Dez 2003 4 Beiträge |
#8
Was die Unterschiede zwischen CFB und OFB angeht, werde ich mich ganz einfach auf deine
Aussage verlassen (ich selber kann das kaum einschätzen) Einen CFB64-Mode selbstzubauen, scheint mir im Moment noch recht kompliziert zusein, da ich mich mit den Themen "Padding" und "Cipher Text Stealing" nicht wirklich auskenne. Ich denke mal, der CFB-Modus (8bit) reicht mir erst mal aus. Es sind ja nur Text-Dateien, die von der Grösse her meistens unter 1MB liegen. Gruß Andreas PS: ich würde zu gern 'n Blick in die inoffizielle DEC Version riskieren. |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |