Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#8

Re: String mit Blowfish verschlüsseln

  Alt 4. Nov 2007, 15:44
In Version 5.1c, obiger Link geht das so

Delphi-Quellcode:
with TCipher_Blowfish.Create do
try
  Init(Password);
  Result := EncodeBinary(Text, TFormat_Copy);
finally
  Free;
end;
Ich empfehle aber es bischen sicherer und eventuell komfortabler zu machen.

Delphi-Quellcode:
uses DECUti, DECCipher, DECHash, DECFmt, DECRandom;

interface

var
  ACipherClass: TDECCipherClass = TCipher_Blowfish;
  ACipherMode: TCipherMode = cmCFS8;
  AHashClass: TDECHashClass = THash_SHA1;
  ATextFormat: TDECFormatClass = TFormat_MIME64;
  AKDFIndex: LongWord = 1;

function Encrypt(const AText: String; const APassword: String): String; overload;
function Decrypt(const AText: String; const APassword: String): String; overload;

implementation
 
function Encrypt(const AText: String; const APassword: String): String;
var
  ASalt: Binary;
  AData: Binary;
begin
  with ValidCipher(ACipherClass).Create, Context do
  try
    ASalt := RandomBinary(16);
    Mode := ACipherMode;
    Init(ValidHash(AHashClass).KDFx(APassword, ASalt, KeySize, TFormat_Copy, AKDFIndex));
    AData := ASalt + EncodeBinary(AText) + CalcMAC;
    Result := ValidFormat(ATextFormat).Encode(AData);
  finally
    Free;
    ProtectBinary(ASalt);
    ProtectBinary(AData);
  end;
end;

function Decrypt(const AText: String; const APassword: String): String;
var
  ASalt: Binary;
  AData: Binary;
  ACheck: Binary;
  ALen: Integer;
begin
  with ValidCipher(ACipherClass).Create, Context do
  try
    ASalt := ValidFormat(ATextFormat).Decode(AText);
    ALen := Length(ASalt) -16 -BufferSize;
    AData := System.Copy(ASalt, 17, ALen);
    ACheck := System.Copy(ASalt, ALen +17, BufferSize);
    SetLength(ASalt, 16);
    Mode := ACipherMode;
    Init(ValidHash(AHashClass).KDFx(APassword, ASalt, KeySize, TFormat_Copy, AKDFIndex));
    Result := DecodeBinary(AData);
    if ACheck <> CalcMAC then
      raise Exception.Create('Invalid data');
  finally
    Free;
    ProtectBinary(ASalt);
    ProtectBinary(AData);
    ProtectBinary(ACheck);
  end;
end;

initialization
  RandomSeed; // initialisiere kryptographisch sicheren Zufallsgenerator in DECRandom.pas mit zufälligem Startwert
finalization
end.
Wir benutzen eine randomisierte KDF -> Key Derivation Function -> Schlüsselableitungsfunktion um aus dem Passwort einen sicheren und jedesmal anderen Sessionkey zu erzeugen. Dies schützt das Passwort des Benutzers vor sehr vielen Angriffen, zb. auch Rainbow Tabellen, Choosen Plaintext, Choosen Ciphertext Angriffen usw. Verschlüsselt man die gleichen Daten mit dem gleichen Passwort mehrmals so wird man erkennen das der Output immer komplett anders ist.
Der Vorteil der KDF ist das sie intern einen Hash benutzt, mit einem Salt aus dem Passwort einen randomisierten Sessionkey erzeugt und nicht zuletzt das der erzeugte Sessionkey exakt so lang an Bytes ist wie der Cipher als Key ausnutzen kann. Man reduziert ja bei zu kurzen Keys die Gesamtsicherheit des Ciphers und bei zu langem Passwort vernichtet man zusätzliche Sicherheit da die zusätzlichen Schlüsselbits ncht vom Cipher ausgwertet werden können. Die KDF beseitigt all diese Probleme. Man sollte immer eine KDF benutzen !

Wir benutzen den Ciphermode cmCFS8. Das ist ein Modus der Byteweise verschlüsselt. Wenn Blowfish also 16 Bytes als Blockgröße hat so wird im 1 Bytes Versatz jedes Datenbyte 16 mal verschlüsselt. Das ist besonderst anzuraten bei der Verschlüsselung kurzer Nachrichten, eben Strings.

Wir wandeln die binären Daten nach ihrer Verschlüsselung in ein anders Textformat um, damit es auch über 7Bit Datenkanäle ohne Datenverlust übertragbar ist. In diesem Falle benutzen wir das MIME-Base64 Format. Wurden die Daten manipuliert so wird man das bei der Entschlüsselung mitbekommen. Du kannst zum Debugging auch mal TFormat_HEX benutzen um die Daten als HEX-String dir anschauen zu können. Beim TFormat_Copy bleiben die Daten 1 zu 1 in ihrer binären Form erhalten. Benuzt du TFormat_PGP ist es das Gleiche wir MIME64 nur das hinten dran noch eine 24Bit Prüfsumme drangehangen wird.

Wir benutzen globale initialisierte Variablen, damit kannst du alle wichtigen Parameter der Funktionen einstellen. Das ist dann wichtig wenn man zb. feststellen würde das Blowfish oder SHA1 geknackt wurden. Man kann dann sehr schnell auf andere Verfahren ausweichen.

Durch das Einbinden von DECRandom.pas benutzen alle internen DEC Random Funktionen einen kryptographisch sicheren Zufallsgenerator. In der Initialization rufen wir RandomSeed; auf, das hat eine ähnliche Wirkung wie Randomize; Allerdings werden die Initialisierungsparameter von RandomSeed; dann aus zb. TickCounter und Datum Uhrzeit des Rechners ermittelt. Das ist immer noch nicht absolut sicher. Besser wäre es zb. 1024 Bytes an Keyboard und Mouse Ereignissen zu sammeln, die durch den Anwender "zufällig" erzeugt wurden. Dann mit RandomSeed(ABuffer, SizeOf(ABuffer)) den Zufallsgenerator initialisieren, wobei ABuffer diese Events enthält.

Ab diesem Moment ist das Ganze wirklich sicher, sowohl von den kryptrographischen Verfahren, Protokollen her gesehen wie auch technologisch in der Implementierung dergleichen.

Gruß Hagen

PS: den Source oben habe ich gerade eben eingetippt, es könnten also kleine Tippfehler drinnen sein.
PPS: Über die Variable AKDFIndex kannst du die Schlüsselableitungsfunktion serialisieren. Wenn du also AKDFIndex zb. auf 25769 setzen würdest so werden andere Sessonkeys zum Password erzeugt als mit AKDFIndex = 1 usw. Das ist vorteilhaft wenn man obigen Source in verschiedenen Anwendungen benutzen möchte deren Daten untereinander nicht entschlüsselbar sein sollen. Dann sollten diese Anwendungen unterschiedliche KDF Indizes benutzen.
PPPS: es gibt noch eine Verbesserungsmöglichkeit. Nach .EncodeBinary() kann man mit .CalcMAC(TFormat_Copy); noch eine "Prüfsumme" der verschlüsselten Daten erzeugen lassen. Diese Prüfsumme wird einfach hinten an Result drangehangen. In Decrypt() kann man dann diese Prüfsumme extrahieren und dann nach .DecodeBinary() wieder mit .CalcMAC(TFormat_Copy) == extrahierte Prüfsumme vergleichen. Damit hat man also eine Überprüfung ob die Daten unverfälscht sind bzw. das gleiche Passwort benutzt wurde. Das besondere dieser Prüfsumme ist es das sie sicherer ist als zb. ein Hash oder eine CRC die man über die Daten gezogen hat. Denn diese Prüfsumme ist zufällig -> KDF und abhängig von den unverschlüsselten Daten wie auch den verschlüsselten Daten und vom sicheren internen Status des Ciphers. Soll heisen das das Vorhandensein dieser prüfsumme nicht ausgenutzt werden kann um zb. Brute Force Angriffe zu beschleunigen oder um überhaupt überprüfen zu können ob man das richtige Passwort per Brute Force gefunden hat. Ich habs mal eben gerade noch eingebaut. Man sieht das man mit DEC sehr einfache Sourcen benutzen kann, will man es aber wirklich sicher so muß man schon ein bischen mehr Aufwand treiben.
  Mit Zitat antworten Zitat