AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein mORMot: ZIP-Datei als Datenspeicher mit verschlüsseltem Inhalt
Thema durchsuchen
Ansicht
Themen-Optionen

mORMot: ZIP-Datei als Datenspeicher mit verschlüsseltem Inhalt

Ein Thema von mytbo · begonnen am 28. Jun 2022
Antwort Antwort
mytbo

Registriert seit: 8. Jan 2007
461 Beiträge
 
#1

mORMot: ZIP-Datei als Datenspeicher mit verschlüsseltem Inhalt

  Alt 28. Jun 2022, 22:48
Auch die zweite mORMot Vorstellung hat als Ausgangspunkt diese Frage im Forum. Die erstellte Beispiel-Anwendung kann als Startpunkt eigener Erkundungen dienen. Ziel ist es, so viel Funktionalität wie möglich mit nur wenigen Zeilen Quelltext zu präsentieren. Es geht hier um Konzepte, nicht um eine fertige Copy-Paste Lösung. Für das Beispiel wird mORMot2 verwendet.

Im Anhang befindet sich der Sourcecode und das ausführbare Programm. Disclaimer: Der Sourcecode ist weder getestet noch optimiert. Er sollte mit Delphi ab Version 10.2 funktionieren. Die Benutzung der zur Verfügung gestellten Materialien erfolgt auf eigene Gefahr.

Die erstellte Klasse TImageResourceFile verwaltet Bilder und verwendet als Speicher eine ZIP-Datei. Die Einträge können optional AES verschlüsselt abgelegt werden. Im zweiten Teil wird die Anbindung einer Fortschrittsanzeige mit Hilfe eines Mediators vorgestellt. Zum Ende zeige ich einige praktische Funktionen und die Ergebnisse einer kleinen Write-Speed-Challenge zwischen Delphi- und mORMot-ZIP, mit einem für mich unerwarteten Ergebnis.

Das Interface der Klasse TImageResourceFile ist sehr einfach gehalten und umfasst nur wenige Funktionen:
Delphi-Quellcode:
TImageResourceFile = class(TObject)
private
  FPassword: RawUtf8;
  FFileName: TFileName;
  FIsWritable: Boolean;
protected
  function LoadStream(pmStream: TCustomMemoryStream; const pmcResName: TFileName; const pmcPassword: RawUtf8): Boolean;
  procedure SaveStream(pmStream: TCustomMemoryStream; const pmcResName: TFileName; const pmcPassword: RawUtf8; pmIsCompressed: Boolean = True);
public
  constructor Create(const pmcFileName: TFileName; const pmcPassword: RawUtf8);
  destructor Destroy; override;
  function LoadImage(pmImage: TImage; const pmcImageName: String): Boolean;
  procedure SaveImage(pmImage: TImage; const pmcImageName: String); overload;
  procedure SaveImage(pmImageData: TCustomMemoryStream; const pmcImageName: String); overload;
end;
Mit dem Beispiel Quelltext bekommt man:
  • Eine ZIP-Datei als Datenablage, die Einträge optional AES verschlüsselt speichern kann.
  • Anbindung einer Fortschrittsanzeige mit Hilfe eines Mediators.
  • Vorstellung einer Funktion, die eine Datei AES ver- und entschlüsseln kann.
  • Beschleunigung beim Speichern und Lesen der Grafik-Formate JPEG, PNG, GIF, TIFF.
Beschleunigung beim Speichern und Lesen von Bildern
Um den letzten Punkt gleich abzuhandeln, hierzu ein paar Benchmark-Werte aus der Anwendung ermittelt mit einem 2MB großen PNG-Bild:
Funktion / Unit NameVcl.Imaging.pngimagemormot.ui.gdiplus
SaveImage()
900 ms
25 ms
LoadImage()
70 ms
10 ms
Die Zahlen sprechen für sich. Bei einem Problem mit der Geschwindigkeit Folgendes probieren: Unit Vcl.Imaging.pngimage entfernen, danach Unit mormot.ui.gdiplus einbinden und die Funktion RegisterSynPictures aufrufen.

ZIP-Datei Implementierung
Durch die Einbindung der Unit mormot.core.zip erhält man Support für ZIP und GZ. Lesen und Schreiben einer ZIP-Datei werden immer getrennt behandelt. Für das Lesen ist die Klasse TZipRead vorhanden und für das Schreiben die Klasse TZipWrite. Die Quelle kann eine Datei oder ein Stream sein. TZipRead kann auch direkt aus einer Ressource, eingebettet in die ausführbare Datei, lesen. Zusätzlich beinhaltet die Unit einige nützliche Funktionen. Beispielhaft sind:
  • ZipTest: Diese Funktion entpackt den Inhalt einer ZIP-Datei und überprüft den jeweiligen CRC.
  • CompressZLib: Ver- oder entschlüsselt den Inhalt unter Verwendung des ZIP Algorithmus.
  • CompressGZip: Ver- oder entschlüsselt den Inhalt unter Verwendung des GZ Algorithmus.
  • FileAppend: Fügt einer ausführbaren Datei eine ZIP-Datei hinzu.
Fortschrittsanzeige mit Mediator
Mediatoren sind eine elegante Möglichkeit Funktionalität zu kapseln und die einfache und sichere Anwendung zu erreichen. Auch wenn das folgende Beispiel unterkomplex ist, ist der Gewinn erkennbar. Hinter der Funktion könnte sich auch ein semi-modaler Dialog mit Fortschrittsanzeige verbergen. Und wer weiß immer, welche Felder genau PProgressInfo enthält? Mit der einfachen Anbindung an einen Mediator erledigt sich das von selbst. Ich wähle nur aus, der Rest wird zuverlässig erledigt. Einmal entwickelt und immer wieder genutzt.
Delphi-Quellcode:
type
  TZipProgressBarGuiHelper = class(TCustomZipProgressGuiHelper)
  private
    FProgressBar: TProgressBar;
  protected
    procedure DoOnInfoProgress(pmSender: TObject; pmInfo: PProgressInfo); override;
  public
    constructor Create(pmProgressBar: TProgressBar); reintroduce;
    procedure Prepare(pmZip: TZipAbstract); override;
  end;
  
procedure TZipProgressBarGuiHelper.DoOnInfoProgress(pmSender: TObject; pmInfo: PProgressInfo);
begin
  if pmInfo.CurrentSize = 0 then
  begin
    FProgressBar.Min := 0;
    FProgressBar.Max := 100;
    FProgressBar.Position := 0
  end
  else if pmInfo.CurrentSize >= pmInfo.ExpectedSize then
    FProgressBar.Position := 0
  else
    FProgressBar.Position := pmInfo.Percent;
end;

procedure TZipProgressBarGuiHelper.Prepare(pmZip: TZipAbstract);
begin
  if pmZip = Nil then Exit; //=>

  pmZip.ReportDelay := 50;
  pmZip.OnProgress := DoOnInfoProgress;
end;
Die Umsetzung der Anbindung ist ein einfacher Aufruf der Funktion Prepare():
Delphi-Quellcode:
var zipWrite: TZipWrite := TZipWrite.Create(MakePath([Executable.ProgramFilePath, 'TestFile.zip']));
try
  FProgressHelper.Prepare(zipWrite);
  zipWrite.AddDeflated(fileName);
finally
  zipWrite.Free;
end;
**Der Begriff Mediator wird hier allgemeiner verwendet, als es der Kontext des gleichnamigen Design-Patterns vorgibt.

Nützliche kryptografische Funktionen
Das Verzeichnis crypt beherbergt die kryptografischen Klassen und Funktionen. Insgesamt umfasst es ca. 28K Zeilen Quelltext. Dreh- und Angelpunkt ist die Unit mormot.crypt.core. Ein kurzer Auszug aus der Beschreibung gibt einen Einblick in den Inhalt:
  • AES Encoding/Decoding with optimized asm and AES-NI/CLMUL support
  • AES-256 Cryptographic Pseudorandom Number Generator (CSPRNG)
  • SHA-2 SHA-3 Secure Hashing
  • HMAC Authentication over SHA and CRC32C
  • PBKDF2 Key Derivation over SHA2 and SHA3
Eine dieser vielen Funktionen ist AesPkcs7File. Mit ihr lässt sich eine Datei sicher ver- oder entschlüsseln. Einmal verschlüsseln und zurück sieht wie folgt aus:
Delphi-Quellcode:
AesPkcs7File('FileName.txt', 'FileName.dat', True, 'TopSecretPassword');
AesPkcs7File('FileName.dat', 'FileName2.txt', False, 'TopSecretPassword');
if HashFileMd5('FileName.txt') = HashFileMd5('FileName2.txt') then
  ShowMessage('Everything is fine!');
Die Funktion CryptDataForCurrentUser kann Daten mittels AES-256-CFB und einem nur dem aktuellen Benutzer bekannten Geheimnis schützen. Für dessen Erstellung wird Windows DPAPI verwendet und die Datei im lokalen AppData-Ordner des Benutzers gespeichert. Zusätzlich kann in der Anwendung noch ein anwendungsspezifischer AppSecret-Wert angegeben werden.
Delphi-Quellcode:
var
  plainText, secretText: RawByteString;
begin
  plainText := 'This is a secret text that only I should know.';
  secretText := CryptDataForCurrentUser(plainText, 'AppSecret', True);

  plainText := CryptDataForCurrentUser(secretText, 'AppSecret', False);
  try
    ShowMessage(Utf8ToString(plainText));
  finally
    FillZero(plainText);
  end;
end;
Mit der Kombination aus den Klassen TAesPkcs7Reader/TAesPkcs7Writer und den Funktionen RecordLoadJson/RecordSaveJson ein Lizenzhandling erstellen:
Delphi-Quellcode:
type
  TLicenseData = record
    CustomerNum: Integer;
    CustomerName: RawUtf8;
    CustomerAddress: RawUtf8;
    LicenceDate: TDate;
    ProductName: RawUtf8;
    ProductVersion: record
      Major: Integer;
      Minor: Integer;
    end;
  end;

var
  licData: TLicenseData;
begin
  licData.CustomerNum := 1;
  licData.CustomerName := 'Thomas';
  licData.LicenceDate := Date;
  licData.ProductName := 'Delphi';
  licData.ProductVersion.Major := 11;
  licData.ProductVersion.Minor := 1;

  var tmpStream: TMemoryStream := TMemoryStream.Create;
  try
    var licStream: TRawByteStringStream := TRawByteStringStream.Create(RecordSaveJson(licData, TypeInfo(TLicenseData)));
    try
      var aesWriter: TAesPkcs7Writer := TAesPkcs7Writer.Create(tmpStream, 'TopSecretPassword');
      try
        StreamCopyUntilEnd(licStream, aesWriter);
        aesWriter.Finish;
      finally
        aesWriter.Free;
      end;
    finally
      licStream.Free;
    end;

    tmpStream.SaveToFile('LicenseData.lic');
  finally
    tmpStream.Free;
  end;
Die JSON Deserialisierung ist fehlertolerant! Das heißt, man kann dem Record TLicenseData neue Felder hinzufügen und die Funktion RecordLoadJson lädt auch vorherige Versionen zuverlässig. Damit muss man keine Streaming-Versionen mehr pflegen.

Write-Speed-Challenge
Ein paar Benchmark-Werte mit Hilfe der Beispiel-Anwendung ermittelt:
Bibliothek - Klasse / Dateigröße1 MB10 MB100 MB
mORMot TZipWrite
35 ms
349 ms
2,8 s
Delphi TZipFile
38 ms
362 ms
2,9 s
Die ZIP Implementierung von mORMot ist nur unwesentlich schneller als die von Delphi. Da hat Embarcadero seine Hausaufgabe gemacht. mORMot unterstützt zwar Delphi 7 bis 11.1, dazu noch den FP-Compiler, aber auch für Delphi gilt: Besser spät als nie.

Zusammenfassung
mORMot ist gut dokumentiert. Die Hilfe umfasst mehr als 2500 Seiten. Davon enthalten die ersten ca. 650 Seiten einen sehr lesenswerten allgemeinen Teil, der Rest ist API Dokumentation. mORMot muss nicht in der IDE installierten werden! Es reicht aus, die entsprechenden Bibliothekspfade einzufügen. Es stehen viele Beispiele und ein freundliches Forum zur Verfügung. Wenn mehr Interesse an mORMot besteht, kann ich auch andere Teile in ähnlicher Weise kurz vorstellen.

Bis bald...
Thomas
Angehängte Dateien
Dateityp: zip TestCryptAndZipSource.zip (1,02 MB, 53x aufgerufen)
Dateityp: zip TestCryptAndZipExe.zip (1,29 MB, 37x aufgerufen)
  Mit Zitat antworten Zitat
Antwort Antwort


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 16:56 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