AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi MemIniCrypt: Vollverschlüsseltes Arbeiten mit ini-Dateien - Kommentare erwünscht!
Thema durchsuchen
Ansicht
Themen-Optionen

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

Ein Thema von CodeX · begonnen am 17. Jan 2011 · letzter Beitrag vom 23. Jan 2011
Antwort Antwort
Seite 1 von 2  1 2      
CodeX

Registriert seit: 30. Okt 2004
471 Beiträge
 
Delphi 12 Athens
 
#1

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

  Alt 17. Jan 2011, 18:38
Delphi-Version: XE
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.



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.
Miniaturansicht angehängter Grafiken
screenshot.png  
Angehängte Dateien
Dateityp: pas MemIniCrypt.pas (9,5 KB, 51x aufgerufen)
Dateityp: zip MemIniCryptTest.zip (2,5 KB, 59x aufgerufen)
Nur Delphi schafft es, einem ein Lächeln zu schenken, wenn man sich beim Schreiben von := vertippt und stattdessen ein :) erscheint.

Geändert von CodeX (17. Jan 2011 um 22:50 Uhr)
  Mit Zitat antworten Zitat
mkinzler
(Moderator)

Registriert seit: 9. Dez 2005
Ort: Heilbronn
39.851 Beiträge
 
Delphi 11 Alexandria
 
#2

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

  Alt 17. Jan 2011, 20:00
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
Markus Kinzler
  Mit Zitat antworten Zitat
gammatester

Registriert seit: 6. Dez 2005
999 Beiträge
 
#3

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

  Alt 17. Jan 2011, 21:04
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.
  Mit Zitat antworten Zitat
CodeX

Registriert seit: 30. Okt 2004
471 Beiträge
 
Delphi 12 Athens
 
#4

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

  Alt 17. Jan 2011, 22:59
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.

Und Du weißt, daß das DCPCrypt-RC4 buggy ist.
Nein, woher? Und inwiefern ist das buggy bzw. wie macht sich das bemerkbar?
Nur Delphi schafft es, einem ein Lächeln zu schenken, wenn man sich beim Schreiben von := vertippt und stattdessen ein :) erscheint.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.134 Beiträge
 
Delphi 12 Athens
 
#5

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

  Alt 18. Jan 2011, 07:19
Im Forum liegt irgendwo ein RCx (von Hagen Reddmann / negaH) rum, in welchem einige Problemchen von RC4 behoben wurden.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
gammatester

Registriert seit: 6. Dez 2005
999 Beiträge
 
#6

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

  Alt 18. Jan 2011, 08:18
Zitat von CodeX:
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 von CodeX:
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).
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.134 Beiträge
 
Delphi 12 Athens
 
#7

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

  Alt 18. Jan 2011, 08:24
Wenn er das Passwort eh in der EXE mit speichert, muß es garnicht 100% sicher sein, wie hier schon jemand angemerkt hatte.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
gammatester

Registriert seit: 6. Dez 2005
999 Beiträge
 
#8

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

  Alt 18. Jan 2011, 08:42
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 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.
  Mit Zitat antworten Zitat
Benutzerbild von Deep-Sea
Deep-Sea

Registriert seit: 17. Jan 2007
907 Beiträge
 
Delphi XE2 Professional
 
#9

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

  Alt 18. Jan 2011, 09:18
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 ...
Chris
Die Erfahrung ist ein strenger Schulmeister: Sie prüft uns, bevor sie uns lehrt.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.134 Beiträge
 
Delphi 12 Athens
 
#10

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

  Alt 18. Jan 2011, 12:21
Darum hatte Hagen auch in RCx einige Änderungen einfließen lassen.
http://www.delphipraxis.net/140859-r...tml#post955578
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:06 Uhr.
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