Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht! (https://www.delphipraxis.net/157592-meminicrypt-vollverschluesseltes-arbeiten-mit-ini-dateien-kommentare-erwuenscht.html)

CodeX 17. Jan 2011 18:38

Delphi-Version: XE

MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Liste der Anhänge anzeigen (Anzahl: 3)
Auf der Suche nach einer Möglichkeit, die ini mit allen Einstellungen vollständig zu verschlüsseln, habe ich leider keine ideale Lösung gefunden. Alles was ich ausprobiert habe, war entweder nicht vollständig nutzbar, nicht Unicode-kompatibel, konnte die Verschlüsselung nicht deaktivieren oder erlaubte keinen mehrfachen Zugriff auf eine ini (z.B. Prozeduraufruf, der auf die ini zugreift, während sie außerhalb bereits bearbeitet wird). Ich habe mich deshalb daran gemacht, eine eigene Lösung zu entwickeln. Herausgekommen ist die Unit MemIniCrypt. Um diese verwenden zu können wird allerdings noch das freie DCPcrypt v2 benötigt. Die Unit unterstützt damit gleichzeitig auch alle Verschlüsselungsmethoden, die dort vorhanden sind. In meiner Implementierung nehme ich RC4, weil das nach meinen Tests am schnellsten arbeitete.
  • Ich würde mich freuen, wenn ein paar Kommentare zur Unit kommen würden. Speziell konkrete Verbesserungsvorschläge (Geschwindigkeitsoptimierung, Code-Patzer, etc.).
  • Außerdem habe ich noch keine Lösung dafür, wie mit falschen Passwörtern umgegangen werden soll, wenn die MemIniCrypt erzeugt wird. Momentan wird der nicht entschlüsselbare Inhalt verworfen. Dies ist für meinen Einsatzzweck irrelevant, da ich sowieso mit einem festen Schlüssel arbeiten werde, was einfach einfach eine direkte Bearbeitung der ini unmöglich machen soll. Wer meint, dass gehört da noch rein und möchte dies umsetzen, werde ich eine vernünftige Lösung gerne mit übernehmen.
  • Was mir definitiv noch fehlt, ist das Einstreuen von Saltz in die Verschlüsselung. Leider war ich mir nicht sicher, wie das optimalerweise umgesetzt werden sollte. Wenn dies jemand beisteuern möchte, würde ich mich sehr freuen!
Zum Testen habe ich ein kleines Programm erstellt, dass alle Hauptfunktionen verwendet und auch ein paar Zeitmessungen durchführt. Generell ist MemIniCrypt nur minimal langsamer als MemIniFile und selbst bei gesetztem Consistent-Parameter (damit jeder Schreibzugriff sofort ausgeführt wird) immer noch deutlich schneller als eine normale IniFile.

Die Unit und die Testdatei befinden sich im Anhang.

http://www.delphipraxis.net/attachme...1&d=1295289138

Delphi-Quellcode:
// MemIniCrypt
//   allows working with fully encrypted ini files
//
// By CodeX, v1.0, 2011-01-17
//
// Features:
// - Allows all encryption methods available in DCPcrypt (by default RC4)
// - Fully Unicode (UTF8) compatible
// - Use with or without encryption
// - Allows optional shared/nested ini access (Consistent=true) or as Xzibit would say:
//   "Sup Dawg, we heard you like shared ini access, so we put an extra UpdateFile
//    into MemIniCrypt, so you can access the ini while you access the ini." ;)
//
// Additional functions compared to TMemIniFile:
//   public Encrypt, Decrypt, IsEncrypted
//   global IsCorrectPassword, IsIniStructure
//
// Requires a Unicode version of Delphi
//   ANSI versions (older than 2009) probably won't work correctly, but were not tested
//
// Requires DCPcrypt v2
//   http://www.cityinthesky.co.uk/cryptography.html
// Inspired by RCmxIni
//   http://www.delphipraxis.net/303502-post2.html

unit MemIniCrypt;

interface

uses
  Classes, sysUtils, IniFiles, DCPRC4, DCPSHA1;

type
  TMemIniCrypt = class(TMemIniFile)
  private
    FFileName: String;
    FPassword: String;
    FEncrypted: Boolean;
    FConsistent: Boolean;
    procedure LoadValues;
  protected
  public
    constructor Create(const FileName, Password: String;
      Consistent: Boolean = true);
    procedure WriteString(const Section, Ident, Value: String);
    procedure UpdateFile; override;
    procedure Rename(const FileName: String; Reload: Boolean);
    procedure EraseSection(const Section: String);
    procedure DeleteKey(const Section, Ident: String);
    procedure SetStrings(List: TStrings; ConsistentAware: Boolean = true);
    destructor Destroy; override;
    function Encrypt(Password: String): Boolean;
    function Decrypt: Boolean;

  end;

function MICIsCorrectPassword(const Filename, Password: String): Boolean;
function MICIsIniStructure(var List: TStringList): Boolean;
function MICIsEncrypted(const Filename: String): Boolean;

implementation

constructor TMemIniCrypt.Create(const FileName, Password: String;
  Consistent: Boolean = true);
var
  bEncrypted : Boolean;
begin
  FFileName  := FileName;
  FPassword  := Password;
  FEncrypted := Password <> '';
  FConsistent := Consistent; //allows shared/nested access, but is significantly slower

  bEncrypted := MICIsEncrypted(FileName);

  if (not bEncrypted) and (FPassword <> '') then
    Encrypt(FPassword)
  else if bEncrypted and (FPassword = '') then
  begin
    // ToDo: How to handle missing passwords for encrypted files
    // (or what to do if PW is not suitable for that ini?)
    // Attention! Using a wrong PW or no encryption (blank PW)
    // will currently erase all existing information!
  end;

  if FEncrypted then
  begin
    // Clean instancing without any values
     inherited Create(''); //inherited Create(FFileName);
  end
  else
  begin
    inherited Create(FFileName, TEncoding.UTF8);
    Encoding := TEncoding.UTF8;
  end;

  // Custom LoadValues
  LoadValues;
end;

destructor TMemIniCrypt.Destroy;
begin
  if not FConsistent then //Only save to file if not already done
    UpdateFile;

  FPassword := '';
  FFilename := '';
  inherited;
end;

procedure TMemIniCrypt.WriteString(const Section, Ident, Value: String);
begin
  inherited;

  // Save to file after each change to allow shared/nested ini access
  if FConsistent then
    UpdateFile;
end;

procedure TMemIniCrypt.EraseSection(const Section: String);
begin
  inherited;

  if FConsistent then
    UpdateFile;
end;

procedure TMemIniCrypt.DeleteKey(const Section, Ident: String);
begin
  inherited;

  if FConsistent then
    UpdateFile;
end;

procedure TMemIniCrypt.SetStrings(List: TStrings;
  ConsistentAware: Boolean = true);
begin
  inherited SetStrings(List);

  // ConsistantAware is required to not update the file when used by LoadValues
  if FConsistent and ConsistentAware then
    UpdateFile;
end;

procedure TMemIniCrypt.Rename(const FileName: String; Reload: Boolean);
begin

  FFileName := FileName;
  if Reload then
    LoadValues;
end;

procedure TMemIniCrypt.LoadValues;
var
  List: TStringList;
  Cipher: TDCP_RC4;
  fsIn: TFileStream;
  fsOut: TMemoryStream;
begin
  if not FEncrypted then
    inherited
  else
  begin
    if (FFileName <> '') and FileExists(FFileName) then
    begin
      List := TStringList.Create;
      Cipher := TDCP_RC4.Create(nil);
      Cipher.InitStr(FPassword, TDCP_SHA1);
      fsIn := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone);
      fsOut := TMemoryStream.Create();
      try
        fsIn.Seek(0, soFromBeginning);
        Cipher.DecryptStream(fsIn, fsOut, fsIn.Size);
        fsOut.Seek(0, soFromBeginning);

        List.LoadFromStream(fsOut, TEncoding.UTF8);
        SetStrings(List, false);
      finally
        List.Free;
        fsIn.Free;
        fsOut.Free;
        Cipher.Burn;
        Cipher.Free;
      end;
    end
    else
      Clear;
  end;
end;

procedure TMemIniCrypt.UpdateFile;
var
  List: TStringList;
  Cipher: TDCP_RC4;
  fsIn: TMemoryStream;
  fsOut: TFileStream;
begin
  if not FEncrypted then
    inherited
  else
  begin
    List := TStringList.Create;
    Cipher := TDCP_RC4.Create(nil);
    fsOut := TFileStream.Create(FFileName, fmCreate);
    fsIn := TMemoryStream.Create;
    try
      Cipher.InitStr(FPassword, TDCP_SHA1);
      GetStrings(List);
      List.SaveToStream(fsIn, TEncoding.UTF8);

      fsIn.Seek(Length(TEncoding.UTF8.GetPreamble), soFromBeginning);
      Cipher.EncryptStream(fsIn, fsOut, fsIn.Size - Length(TEncoding.UTF8.GetPreamble));
    finally
      List.Free;
      fsIn.Free;
      fsOut.Free;
      Cipher.Burn;
      Cipher.Free;
    end;
  end;
end;

function TMemIniCrypt.Encrypt(Password: String): Boolean;
var
  Cipher: TDCP_RC4;
  fsIn: TFileStream;
  fsOut: TMemoryStream;
begin
  Result := false;

  if length(Password) = 0 then
    Exit;

  if not((FFileName <> '') and FileExists(FFileName)) then
    Exit;

  if MICIsEncrypted(FFileName) then
    Exit;

  Cipher := TDCP_RC4.Create(nil);
  fsOut := TMemoryStream.Create;
  try
    Cipher.InitStr(FPassword, TDCP_SHA1);

    fsIn := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone);
    try
      fsIn.Seek(0, soFromBeginning);
      Cipher.EncryptStream(fsIn, fsOut, fsIn.Size);
    finally
      fsIn.Free;
    end;

    fsOut.Seek(0, soFromBeginning);
    fsOut.SaveToFile(FFileName);

    FPassword := Password;
    FEncrypted := true;
    Result := true;
  finally
    Cipher.Burn;
    Cipher.Free;
    fsOut.Free;
  end;
end;

function TMemIniCrypt.Decrypt: Boolean;
var
  Cipher: TDCP_RC4;
  fsIn: TFileStream;
  fsOut: TMemoryStream;
  List: TStringList;
  i: Integer;
begin
  Result := false;

  if not((FFileName <> '') and FileExists(FFileName)) then
    Exit;

  if not MICIsEncrypted(FFileName) then
    Exit;

  Cipher := TDCP_RC4.Create(nil);
  fsOut := TMemoryStream.Create;
  try
    Cipher.InitStr(FPassword, TDCP_SHA1);

    fsIn := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone);
    try
      fsIn.Seek(0, soFromBeginning);
      Cipher.DecryptStream(fsIn, fsOut, fsIn.Size);
    finally
      fsIn.Free;
    end;

    List := TStringList.Create;
    try
      fsOut.Seek(0, soFromBeginning);
      List.LoadFromStream(fsOut);

      // Only save if the file was encrypted correctly
      if MICIsIniStructure(List) then
        fsOut.SaveToFile(FFileName);
    finally
      List.Free;
    end;

    FEncrypted := false;
    Result := true;
  finally
    Cipher.Burn;
    Cipher.Free;
    fsOut.Free;
  end;
end;

function MICIsEncrypted(const Filename: String): Boolean;
var
  fs: TFileStream;
  List: TStringList;
begin
  Result := false;

  if not((Filename <> '') and FileExists(Filename)) then
    Exit;

  List := TStringList.Create;
  try
    fs := TFileStream.Create(Filename, fmOpenRead or fmShareDenyNone);
    try
      fs.Seek(0, soFromBeginning);
      List.LoadFromStream(fs);
      Result := not MICIsIniStructure(List);
    finally
      fs.Free;
    end;
  finally
    List.Free;
  end;
end;

function MICIsCorrectPassword(const Filename, Password: String): Boolean;
var
  List: TStringList;
  Cipher: TDCP_RC4;
  fsIn: TFileStream;
  fsOut: TMemoryStream;
begin
  Result := false;

  if not((Filename <> '') and FileExists(Filename)) then
    Exit;

  List := TStringList.Create;
  Cipher := TDCP_RC4.Create(nil);
  fsIn := TFileStream.Create(Filename, fmOpenRead or fmShareDenyNone);
  fsOut := TMemoryStream.Create();
  try
    fsIn.Seek(0, soFromBeginning);
    Cipher.InitStr(Password, TDCP_SHA1);
    Cipher.DecryptStream(fsIn, fsOut, fsIn.Size);
    fsOut.Seek(0, soFromBeginning);
    List.LoadFromStream(fsOut);
    Result := MICIsIniStructure(List);
  finally
    List.Free;
    fsIn.Free;
    fsOut.Free;
    Cipher.Burn;
    Cipher.Free;
  end;
end;

function MICIsIniStructure(var List: TStringList): Boolean;
var
  i: Integer;
begin
  if List.Count > 0 then
  begin
    Result := false;
    for i := 0 to List.Count - 1 do
    begin
      if copy(List[i], 0, 1) + copy(List[i], Length(List[i]), 1) = '[]' then
      begin
        Result := true;
        Break;
      end;
    end;
  end;
end;

end.

mkinzler 17. Jan 2011 20:00

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Zitat:

edit: Kann ein Mod bitte das überflüssige "d" am Ende der Überschrift entfernen? Kann man als normaler User wohl leider nicht selbst...
Sollte auch als normaler Benutzer im erweiterten Editor gehen.
Hab es aber mal wie gewünscht korrigiert

gammatester 17. Jan 2011 21:04

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Du weißt aber schon, daß Stromchiffren mit konstantem Passwort ohne Initialisierungsvektor brutal unsicher sind? Und Du weißt, daß das DCPCrypt-RC4 buggy ist?

Wenn Du kein Salz oder IV hast, solltest Du auf keinen Fall eine Stromchiffre verwenden oder eine Blockcipher in den Modi CTR, OFB, ECB.

CodeX 17. Jan 2011 22:59

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Zitat:

Zitat von gammatester (Beitrag 1075376)
Du weißt aber schon, daß Stromchiffren mit konstantem Passwort ohne Initialisierungsvektor brutal unsicher sind?

Naja, genau das war ja der Grund meiner Bitte, ob nicht jemand helfen kann, den Code um eine effektive und effiziente Salz-Funktionalität zu erweitern.
Ganz generell ist mein Szenario so, dass ein Administrator das Programm konfigurieren kann und diverse Nutzer dies anschließend verwenden sollen, ohne an den Einstellungen herumzuspielen. Deswegen soll die ini nicht im Klartext vorliegen. Dafür wird das Passwort so oder so in das Programm hardcodiert. Wir sprechen also keineswegs von Top-Sicherheit. Damit die ersten verschlüsselten Abschnitte aber nicht immer gleich aussehen, wäre ein bisschen Salz dennoch hilfreich, wo ich mir aber eben wie beschrieben nicht sicher bin, wie das am besten zu implementieren ist.
Wenn jemand einen Vorschlag hat, nehme ich diesen sehr gerne mit auf.

Zitat:

Zitat von gammatester (Beitrag 1075376)
Und Du weißt, daß das DCPCrypt-RC4 buggy ist.

Nein, woher? Und inwiefern ist das buggy bzw. wie macht sich das bemerkbar?

himitsu 18. Jan 2011 07:19

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Im Forum liegt irgendwo ein RCx (von Hagen Reddmann / negaH) rum, in welchem einige Problemchen von RC4 behoben wurden.

gammatester 18. Jan 2011 08:18

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Zitat:

Zitat von CodeX
Zitat:

Zitat von gammatester
Du weißt aber schon, daß Stromchiffren mit konstantem Passwort ohne Initialisierungsvektor brutal unsicher sind?

Naja, genau das war ja der Grund meiner Bitte, ob nicht jemand helfen kann, den Code um eine effektive und effiziente Salz-Funktionalität zu erweitern.
[...]
Wenn jemand einen Vorschlag hat, nehme ich diesen sehr gerne mit auf.

Was spricht denn gegen 128-Bit-AES/CBC, wobei Du für jeden Eintrag, den Du verschlüsselst, einen Zufalls-IV nimmst und diesen mit abspeicherst?
Zitat:

Zitat von CodeX
Zitat:

Zitat von gammatester
Und Du weißt, daß das DCPCrypt-RC4 buggy ist.

Nein, woher? Und inwiefern ist das buggy bzw. wie macht sich das bemerkbar?

Liest Du die von Die verlinkten Seite eigentlich? Im ersten Absatz steht es dick und fett da.

@himitsu: Das löst doch das Problem hier überhaupt nicht und kann es auch gar nicht: Der Schüsselstrom ist doch immer der gleiche, auch ein "OTP" ist in dieser Situation unsicher (weil es eigentlich ein "MTP" ist).

himitsu 18. Jan 2011 08:24

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Wenn er das Passwort eh in der EXE mit speichert, muß es garnicht 100% sicher sein, wie hier schon jemand angemerkt hatte. :angle:

gammatester 18. Jan 2011 08:42

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Zitat:

Zitat von CodeX
Ganz generell ist mein Szenario so, dass ein Administrator das Programm konfigurieren kann und diverse Nutzer dies anschließend verwenden sollen, ohne an den Einstellungen herumzuspielen. Deswegen soll die ini nicht im Klartext vorliegen.

Man kann auch an der verschlüsselten Datei rumspielen.
Zitat:

Zitat von CodeX
Damit die ersten verschlüsselten Abschnitte aber nicht immer gleich aussehen,...

Warum stört Dich das? Was Du eigentlich willst, ist doch nicht unbedingt Verschlüsselung sondern Authentizität. Die erhältst Du (auf dem Level mit "Passwort in EXE"), indem Du das Passwort z.B. für ein HMAC verwendest. Oder, wenn Du beides haben willst, nimm doch einfach den EAX-Modus.

Deep-Sea 18. Jan 2011 09:18

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Zitat:

Zitat von gammatester (Beitrag 1075415)
Man kann auch an der verschlüsselten Datei rumspielen.

Und gerade bei einer Stromchiffre, denn da kann man jedes Bit einzeln und unabhängig von allen anderen ändern. Wenn man also weiß, das an einer bestimmten Stelle etwas steht, kann man es manipulieren ...

himitsu 18. Jan 2011 12:21

AW: MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
 
Darum hatte Hagen auch in RCx einige Änderungen einfließen lassen.
http://www.delphipraxis.net/140859-r...tml#post955578


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:05 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz