Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Shop-Zugangsdaten verschlüsseln (https://www.delphipraxis.net/197661-shop-zugangsdaten-verschluesseln.html)

freimatz 25. Aug 2018 09:40

Shop-Zugangsdaten verschlüsseln
 
Hallo zusammen,
ich habe eine ähnliche Problematik wie bei DB-Zugangsdaten verschlüsseln. Bei mir geht es aber nicht um Zugangsdaten die nur das Programm kennt sondern um Zugangsdaten die der jeweilige Anwender spezifisch hat.

Es geht um einen Internet-Shop auf den ich zugreifen muss. Dazu benötige ich den Namen und Passwort des Anwenders. Also fordere ich den Anwender auf diese einzugeben und mein Programm macht was es tun muss. Nun soll er das nicht jedesmal tun müssen, dann müssen aber die Daten gespeichert werden. Das ist ja eine Unsicherheit.

Wie geht man da am Besten vor? In einem anderen Thread wo es eher um technische Probleme ging, wurde vorgeschlagen die Daten mit AES verschlüsselt zu speichern. Dazu muss der Key ja auch im Programm fix drin sein. Oder?

Ein ähnliches Problem haben doch auch Browser? Wenn ich mich hier im Forum anmelde, dann hat mein Browser Name und Passwort auch gespeichert. Nicht sehr vertrauenserweckend wenn man z.B. so was liest: https://www.heise.de/security/meldun...t-3998599.html). Drum mache ich beim Online-Banking u.a. das grundsätzlich nicht.

Gut wäre es auch wenn ich den Anwender nicht zwinge das Passwort zu speichern sondern es ihm überlasse. Wenn er nicht will muss er es halt jedesmal eingeben. Oder?

Schokohase 25. Aug 2018 10:23

AW: Shop-Zugangsdaten verschlüsseln
 
Du kannst dafür die Data Protection API verwenden (gibt es seit Windows 2000)

freimatz 25. Aug 2018 10:32

AW: Shop-Zugangsdaten verschlüsseln
 
Danke.
Wofür und an welcher Stelle? Um die Daten des Anwenders zu speichern?

Habe kurz reingeschaut. Wenn ich das richtig sehe ist das eine API für Verschlüsselungen.
Zitat:

DPAPI is a password-based data protection service. It requires a password to provide protection. The drawback, of course, is that all protection provided by DPAPI rests on the password provided.
Wenn ich es richtig verstehe brauche ich dann auch ein Key dazu, der in meinem Programm gespeichert werden muss. Oder?

Schokohase 25. Aug 2018 10:53

AW: Shop-Zugangsdaten verschlüsseln
 
Nein, du brauchst die Daten und eine optionale Entropy (wird auch als Salt bezeichnet).

Der Schlüssel wird von der API selber generiert/verwaltet. Siehe dazu bei dwFlags
Zitat:

CRYPTPROTECT_LOCAL_MACHINE
When this flag is set, it associates the data protected with the current computer instead of with an individual user. Any user on the computer on which the internal protect function is called with this flag can use the internal unprotect function to unprotect the data. Application developers should understand that by using this flag no "real" protection is provided by DPAPI. By "real" we mean that any process running on the system can unprotect any data protected with this flag. We highly recommended that this flag not be used on workstations to protect user's data. It does make sense, however, for a server process to use the flag on a server where untrusted users are not allowed to logon. It also makes sense for a local machine process to use the flag to protect data to be stored off the machine or on a shared drive.
Die Daten können somit nur von dem Benutzer oder auf dieser Maschine wieder entschlüsselt werden.

Hier noch etwas auf stackoverflow

Schokohase 25. Aug 2018 20:16

AW: Shop-Zugangsdaten verschlüsseln
 
So sieht das dann aus, wenn mans es mit einem netten Wrapper versieht (läuft ab Windows 2000 sowohl x32 und x64)
Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Security.Cryptographic.ProtectedData;

procedure SmallTest( );
var
  inStr, outStr: string;
  inBuffer, outBuffer, encrypted: TBytes;
begin
  inStr := 'Some Secret Data Here';
  WriteLn( inStr );
  inBuffer := TEncoding.UTF8.GetBytes( inStr );
  encrypted := TProtectedData.Protect( inbuffer ); // verschlüsseln
  outBuffer := TProtectedData.Unprotect( encrypted ); // entschlüsseln
  outStr := TEncoding.UTF8.GetString( outBuffer );
  WriteLn( outStr );
end;

begin
  try
    SmallTest( );
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  ReadLn;
end.
Delphi-Quellcode:
unit Security.Cryptographic.ProtectedData;

interface

uses
  System.SysUtils;

{$SCOPEDENUMS ON}

type
  TDataProtectionScope = (
    CurrentUser = $00,
    LocalMachine = $01 );

  TProtectedData = class
  strict private
    const
      DefaultDataProtectionScope = TDataProtectionScope.CurrentUser;
  public
    class function Protect( const AUserData: TBytes; AScope: TDataProtectionScope = DefaultDataProtectionScope ): TBytes; overload; static;
    class function Protect( const AUserData: TBytes; const AOptionalEntropy: TBytes; AScope: TDataProtectionScope = DefaultDataProtectionScope ): TBytes; overload; static;
    class function Unprotect( const AEncryptedDataData: TBytes; AScope: TDataProtectionScope = DefaultDataProtectionScope ): TBytes; overload; static;
    class function Unprotect( const AEncryptedDataData: TBytes; const AOptionalEntropy: TBytes; AScope: TDataProtectionScope = DefaultDataProtectionScope ): TBytes; overload; static;
  end;

implementation

{$IFDEF MSWINDOWS}
uses
  Winapi.Windows,
  Winapi.DataProtectApi;
{$ENDIF}

{ TProtectedData }

class function TProtectedData.Protect( const AUserData, AOptionalEntropy: TBytes; AScope: TDataProtectionScope ): TBytes;
{$IFDEF MSWINDOWS}
var
  dataIn, dataOut, entropy: DATA_BLOB;
  pEntropy: PDATA_BLOB;
  flags: DWORD;
begin
  if Length( AUserData ) = 0 then
    raise EArgumentException.Create( 'AUserData' );

  dataOut.cbData := 0;
  dataOut.pbData := nil;

  dataIn.cbData := length( AUserData );
  dataIn.pbData := @AUserData[0];

  if Length( AOptionalEntropy ) > 0 then
  begin
    entropy.cbData := length( AOptionalEntropy );
    entropy.pbData := @AOptionalEntropy[0];
    pEntropy := @entropy;
  end
  else
    pEntropy := nil;

  flags := CRYPTPROTECT_UI_FORBIDDEN;
  if AScope = TDataProtectionScope.LocalMachine then
    flags := flags or CRYPTPROTECT_LOCAL_MACHINE;

  if not CryptProtectData( @dataIn, nil, pentropy, nil, nil, flags, @dataOut ) then
    RaiseLastOsError;

  SetLength( Result, dataOut.cbData );
  move( dataOut.pbData^, Result[0], dataOut.cbData );
  LocalFree( HLOCAL( dataOut.pbData ) );
end;
{$ELSE}
begin
  raise ENotImplementedException.Create( 'Not Implemented' );
end;
{$ENDIF}

class function TProtectedData.Protect( const AUserData: TBytes; AScope: TDataProtectionScope ): TBytes;
var
  Entropy: TBytes;
begin
  Entropy := [];
  Result := TProtectedData.Protect( AUserData, Entropy, AScope );
end;

class function TProtectedData.Unprotect( const AEncryptedDataData, AOptionalEntropy: TBytes; AScope: TDataProtectionScope ): TBytes;
{$IFDEF MSWINDOWS}
var
  dataIn, dataOut, entropy: DATA_BLOB;
  pEntropy: PDATA_BLOB;
  flags: DWORD;
begin
  if Length( AEncryptedDataData ) = 0 then
    raise EArgumentException.Create( 'AEncryptedDataData' );

  dataOut.cbData := 0;
  dataOut.pbData := nil;

  dataIn.cbData := length( AEncryptedDataData );
  dataIn.pbData := @AEncryptedDataData[0];

  if Length( AOptionalEntropy ) > 0 then
  begin
    entropy.cbData := length( AOptionalEntropy );
    entropy.pbData := @AOptionalEntropy[0];
    pEntropy := @entropy;
  end
  else
    pEntropy := nil;

  flags := CRYPTPROTECT_UI_FORBIDDEN;
  if AScope = TDataProtectionScope.LocalMachine then
    flags := flags or CRYPTPROTECT_LOCAL_MACHINE;

  if not CryptUnprotectData( @dataIn, nil, pentropy, nil, nil, flags, @dataOut ) then
    RaiseLastOsError;

  SetLength( Result, dataOut.cbData );
  move( dataOut.pbData^, Result[0], dataOut.cbData );
  LocalFree( HLOCAL( dataOut.pbData ) );
end;
{$ELSE}
begin
  raise ENotImplementedException.Create( 'Not Implemented' );
end;
{$ENDIF}

class function TProtectedData.Unprotect( const AEncryptedDataData: TBytes; AScope: TDataProtectionScope ): TBytes;
var
  Entropy: TBytes;
begin
  Entropy := [];
  Result := Unprotect( AEncryptedDataData, Entropy, AScope );
end;

end.
Delphi-Quellcode:
unit Winapi.DataProtectApi;

interface

uses
  Winapi.Windows;

const
  CRYPTPROTECT_UI_FORBIDDEN = UINT( $01 );
{$EXTERNALSYM CRYPTPROTECT_UI_FORBIDDEN}
  CRYPTPROTECT_LOCAL_MACHINE = UINT( $04 );
{$EXTERNALSYM CRYPTPROTECT_LOCAL_MACHINE}
  CRYPTPROTECT_CRED_SYNC = UINT( $08 );
{$EXTERNALSYM CRYPTPROTECT_CRED_SYNC}
  CRYPTPROTECT_AUDIT = UINT( $010 );
{$EXTERNALSYM CRYPTPROTECT_AUDIT}
  CRYPTPROTECT_NO_RECOVERY = UINT( $020 );
{$EXTERNALSYM CRYPTPROTECT_NO_RECOVERY}
  CRYPTPROTECT_VERIFY_PROTECTION = UINT( $040 );
{$EXTERNALSYM CRYPTPROTECT_VERIFY_PROTECTION}

type
  PCryptoApiBlob = ^TCrypeoApiBlob;
  _CRYPTOAPI_BLOB = record
    cbData: DWORD;
    pbData: PBYTE;
  end;
{$EXTERNALSYM _CRYPTOAPI_BLOB}

  TCrypeoApiBlob = _CRYPTOAPI_BLOB;

  CRYPT_INTEGER_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_INTEGER_BLOB}
  PCRYPT_INTEGER_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_INTEGER_BLOB}
  CRYPT_UINT_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_UINT_BLOB}
  PCRYPT_UINT_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_UINT_BLOB}
  CRYPT_OBJID_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_OBJID_BLOB}
  PCRYPT_OBJID_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_OBJID_BLOB}
  CERT_NAME_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CERT_NAME_BLOB}
  PCERT_NAME_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCERT_NAME_BLOB}
  CERT_RDN_VALUE_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CERT_RDN_VALUE_BLOB}
  PCERT_RDN_VALUE_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCERT_RDN_VALUE_BLOB}
  CERT_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CERT_BLOB}
  PCERT_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCERT_BLOB}
  CRL_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRL_BLOB}
  PCRL_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRL_BLOB}
  DATA_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM DATA_BLOB}
  PDATA_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PDATA_BLOB}
  CRYPT_DATA_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_DATA_BLOB}
  PCRYPT_DATA_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_DATA_BLOB}
  CRYPT_HASH_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_HASH_BLOB}
  PCRYPT_HASH_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_HASH_BLOB}
  CRYPT_DIGEST_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_DIGEST_BLOB}
  PCRYPT_DIGEST_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_DIGEST_BLOB}
  CRYPT_DER_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_DER_BLOB}
  PCRYPT_DER_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_DER_BLOB}
  CRYPT_ATTR_BLOB = _CRYPTOAPI_BLOB;
{$EXTERNALSYM CRYPT_ATTR_BLOB}
  PCRYPT_ATTR_BLOB = PCryptoApiBlob;
{$EXTERNALSYM PCRYPT_ATTR_BLOB}

  PCryptProtectPromptStruct = ^TCryptProtectPromptStruct;
  _CRYPTPROTECT_PROMPTSTRUCT = record
    cbSize: DWORD;
    dwPromptFlags: DWORD;
    hwndApp: HWND;
    szPrompt: LPCWSTR;
  end;
{$EXTERNALSYM _CRYPTPROTECT_PROMPTSTRUCT}

  TCryptProtectPromptStruct = _CRYPTPROTECT_PROMPTSTRUCT;

  PCRYPTPROTECT_PROMPTSTRUCT = ^_CRYPTPROTECT_PROMPTSTRUCT;
{$EXTERNALSYM PCRYPTPROTECT_PROMPTSTRUCT}
  CRYPTPROTECT_PROMPTSTRUCT = _CRYPTPROTECT_PROMPTSTRUCT;
{$EXTERNALSYM CRYPTPROTECT_PROMPTSTRUCT}

function CryptProtectData( pDataIn: PDATA_BLOB; szDataDescr: LPCWSTR; pOptionalEntropy: PDATA_BLOB; pvReserved: PVOID; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB ): BOOL; stdcall;
{$EXTERNALSYM CryptProtectData}
function CryptUnprotectData( pDataIn: PDATA_BLOB; szDataDescr: LPCWSTR; pOptionalEntropy: PDATA_BLOB; pvReserved: PVOID; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB ): BOOL; stdcall;
{$EXTERNALSYM CryptUnprotectData}

const
  crypt32 = 'crypt32.dll';

implementation

function CryptProtectData; external crypt32 name 'CryptProtectData';
function CryptUnprotectData; external crypt32 name 'CryptUnprotectData';

end.

KodeZwerg 25. Aug 2018 20:35

AW: Shop-Zugangsdaten verschlüsseln
 
Danke Schokohase für Deinen Wrapper.
Ich musste zwei Änderungen durchführen damit es funktioniert, deshalb Frage: Sind Änderungen so Okay? (in Demo erscheint 2x gleicher Text)

Änderung #1:
Delphi-Quellcode:
PVOID
in
Delphi-Quellcode:
Pointer

Änderung #2:
Delphi-Quellcode:
Entropy := [];
in
Delphi-Quellcode:
SetLength(Entropy, 0);

Danke für Mühe, ich kannte das noch nicht.

Schokohase 25. Aug 2018 20:44

AW: Shop-Zugangsdaten verschlüsseln
 
Geschrieben habe ich das unter 10.2.3.

Da befindet sich in der Winapi.Windows
Delphi-Quellcode:
  PVOID = Pointer;
  {$EXTERNALSYM PVOID}
Und genau diesen Eintrag würde ich an deiner Stelle in der Unit hinzufügen anstatt in der Deklaration
Delphi-Quellcode:
PVOID
durch
Delphi-Quellcode:
Pointer
zu ersetzen. Dann sieht es exakt so aus wie in der API-Dokumentation.

Delphi-Quellcode:
Entropy = [];
kann man ab Delphi Version X verwenden und ist das gleiche wie
Delphi-Quellcode:
SetLength(Entropy,0);

KodeZwerg 25. Aug 2018 20:48

AW: Shop-Zugangsdaten verschlüsseln
 
Dann ist es also Okay für mein Delphi, vielen Dank für Antwort!:thumb:

edit
Zitat:

Zitat von Schokohase (Beitrag 1411644)
Delphi-Quellcode:
  PVOID = Pointer;
  {$EXTERNALSYM PVOID}
Und genau diesen Eintrag würde ich an deiner Stelle in der Unit hinzufügen

Habe es so durchgeführt, Danke nochmal für flotte Antwort.

freimatz 4. Sep 2018 13:21

AW: Shop-Zugangsdaten verschlüsseln
 
So, jetzt kam ich mal dazu das auszuprobieren. Danke erstmal für den Hinweis und den Code.:thumb:
Diese API kannte ich noch gar nicht. Die hätte ich vor 20 Jahren schon brauchen können.
Der Code hier läuft bei mir hier ohne Probleme durch. (Berlin)

Ein Frage hätte ich noch. Ich beziehe mich auf den Beispielscode:
encrypted := TProtectedData.Protect( inbuffer ); // verschlüsseln

Das encrypted kann da doch Nullen enthalten. Das ist meine Erkenntnis beim Debuggen. Wenn ich das nun in einer INI-Datei o.ä. speichern will, muss ich das nochmals codieren z.B. mit base64.
Sehe ich das so richtig?
(Nein ich brauche kein Beispiel wie base64 geht)

Uwe Raabe 4. Sep 2018 13:38

AW: Shop-Zugangsdaten verschlüsseln
 
Zitat:

Zitat von freimatz (Beitrag 1412424)
encrypted := TProtectedData.Protect( inbuffer ); // verschlüsseln

Das encrypted kann da doch Nullen enthalten. Das ist meine Erkenntnis beim Debuggen. Wenn ich das nun in einer INI-Datei o.ä. speichern will, muss ich das nochmals codieren z.B. mit base64.
Sehe ich das so richtig?

Korrekt. :thumb:

Wie ich das sehe, ist encrypted als TBytes deklariert. Das kann so direkt gar nicht in eine INI-Datei geschrieben werden, sondern muss erst in einen String umgewandelt werden. Da kein TEncoding das für jede beliebige Byte-Sequenz hinbekommt (siehe z.B. Nullen), bietet sich hier sowas wie Base64 geradezu an.

freimatz 4. Sep 2018 13:39

AW: Shop-Zugangsdaten verschlüsseln
 
Danke.

Schokohase 4. Sep 2018 14:37

AW: Shop-Zugangsdaten verschlüsseln
 
Nichts was ein Class Helper nicht lösen könnte

Delphi-Quellcode:
uses
  System.Classes, System.IniFiles, System.SysUtils;

type
  TCustomIniFileHelper = class helper for TCustomIniFile
  public
    function ReadBytes( const Section, Name: string ): TBytes;
    procedure WriteBytes( const Section, Name: string; const Value: TBytes );
  end;

implementation

{ TCustomIniFileHelper }

function TCustomIniFileHelper.ReadBytes( const Section, Name: string ): TBytes;
var
  stream: TBytesStream;
begin
  stream := TBytesStream.Create( );
  try
    Self.ReadBinaryStream( Section, Name, stream );
    Result := stream.Bytes;
  finally
    stream.Free( );
  end;
end;

procedure TCustomIniFileHelper.WriteBytes( const Section, Name: string; const Value: TBytes );
var
  stream: TBytesStream;
begin
  stream := TBytesStream.Create( Value );
  try
    Self.WriteBinaryStream( Section, Name, stream );
  finally
    stream.Free( );
  end;
end;

KodeZwerg 4. Sep 2018 16:14

AW: Shop-Zugangsdaten verschlüsseln
 
*entfernt, ich verstehe TE gerade nicht*

freimatz 4. Sep 2018 16:39

AW: Shop-Zugangsdaten verschlüsseln
 
Tja, Sorry Kathinka, das war für die Katz ;-)
Zitat:

Zitat von freimatz (Beitrag 1412424)
... in einer INI-Datei o.ä. ...

Wenn man eine "INI-Datei" verwendet geht das schon, nicht aber bei "o.ä." :-D
Zitat:

Zitat von freimatz (Beitrag 1412424)
(Nein ich brauche kein Beispiel wie base64 geht)


freimatz 5. Sep 2018 20:31

AW: Shop-Zugangsdaten verschlüsseln
 
Vielleicht doch? ...
Den Code habe ich für meine Bedürfnisse erweitert. Ich bin Jetzt auf XE2. Dank Kodezwerg konnte ich das auch da zum Laufen bringen.
Bei der ersten Erweiterung geht um Base 64. Als Grundlage nahme ich das Beispiel oben mit "procedure SmallTest( );"
Delphi-Quellcode:
uses
  ...
  Soap.EncdDecd,
...
function EncryptStringToBase64(const S: String): String;
var
  inBuffer, outBuffer, encrypted: TBytes;
begin
  inBuffer := TEncoding.UTF8.GetBytes( S );
  encrypted := TProtectedData.Protect( inbuffer ); // verschlüsseln
  Result := EncodeBase64(encrypted,Length(encrypted));
  Result := StringReplace(Result, #13#10, '', [rfReplaceAll]);
end;

function DecryptBase64ToString(const S: String): String;
var outBuffer, encrypted: TBytes;
begin
  encrypted := DecodeBase64(S);
  outBuffer := TProtectedData.Unprotect( encrypted ); // entschlüsseln
  Result := TEncoding.UTF8.GetString( outBuffer );
end;
Dazu habe ich einen unit-test gemacht:
Delphi-Quellcode:
procedure TTest_LoginCrypt.Test_Basic1();
var
  orginal: String;
  crypt: String;
  decrypt: String;
begin
  orginal := 'DesÜsch a Passwörd!';
  crypt := EncryptStringToBase64(orginal);
  decrypt := DecryptBase64ToString(crypt);
  CheckEquals(orginal, decrypt, 'decrypt');
end;
Der läuft gut. Anders sieht es mit dem nächsten aus.
Delphi-Quellcode:
procedure TTest_LoginCrypt.Test_Basic2();
const expected =
  'AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAR6WMUwdNMUWKnnyFGb2XNAAAAAACAAAAAAAQZgAAAAEAACAAAAB54/kNaa'+
  'FIZe84QZZTmPvuZ9asbR6NoCsGdTGhpYDS4AAAAAAOgAAAAAIAACAAAAD44yDX7VmMRv6NO3Sf1fBdJXAd6YmB1HRb'+
  '75aqGm1XCyAAAAAx+BMspmyEA9ymHt02gmt60xXA0JRYzzEtQirpBUSGvUAAAADD6HkiNivbUlSN1LW8293Bq7ktHA'+
  'ca7+3zscNrTPbfqo7zg/cQZLFhIPHVaW8Gqp+fsa9Nx3p4u/XRI0EdsT1n';
var
  orginal: String;
  crypt: String;
  decrypt: String;
begin
  orginal := 'DesÜsch a Passwörd!';
  crypt := EncryptStringToBase64(orginal);
  CheckEquals(expected, crypt, 'crypt');
  decrypt := DecryptBase64ToString(crypt);
  CheckEquals(orginal, decrypt, 'decrypt');
end;
Beim ersten Mal geht er schief, klar denke ich die Daten passen nicht. Ich kopiere mir die richtigen Daten aus dem Debugger oder unit-test Ergebnis raus. Aber bei jedem Durchlauf werden andere Daten erwartet.
Wo ist mein Denkfehler?

Schokohase 5. Sep 2018 20:59

AW: Shop-Zugangsdaten verschlüsseln
 
Weil bei jedem Protect mit einem zusätzlichen (zufälligem) Salt verschlüsselt wird (macht die API von selber). Dadurch ist die Verschlüsselte Code-Folge immer unterschiedlich auch wenn die zu verschlüsselnden Daten gleich sind.

KodeZwerg 5. Sep 2018 22:18

AW: Shop-Zugangsdaten verschlüsseln
 
Zitat:

Zitat von freimatz (Beitrag 1412529)
Vielleicht doch? ...
Den Code habe ich für meine Bedürfnisse erweitert. Ich bin Jetzt auf XE2. Dank Kodezwerg konnte ich das auch da zum Laufen bringen.
Bei der ersten Erweiterung geht um Base 64. Als Grundlage nahme ich das Beispiel oben mit "procedure SmallTest( );"

Also für Base64 nehm ich nix was aus einer Delphi Unit kommt, da bläht sich Datei mit viel ungenutzten Indy methoden auf.
Ich nutze sowas hier, ich wollte es ursprünglich auch als noch zwei weitere Helfer posten, aber ich kam mit Deinem Text nicht so ganz klar.
Hier ist mein Base64 Kompromiss der keine Speziellen Units braucht. Ob schneller oder langsamer als andere kann ich nicht sagen, habs noch nicht gebencht.
Delphi-Quellcode:
const
  Codes64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';

function Encode64(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do
  begin
    x := Ord(s[i]);
    b := b * 256 + x;
    a := a + 8;
    while a >= 6 do
    begin
      a := a - 6;
      x := b div (1 shl a);
      b := b mod (1 shl a);
      Result := Result + Codes64[x + 1];
    end;
  end;
  if a > 0 then
  begin
    x := b shl (6 - a);
    Result := Result + Codes64[x + 1];
  end;
end;

function Decode64(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do
  begin
    x := Pos(s[i], codes64) - 1;
    if x >= 0 then
    begin
      b := b * 64 + x;
      a := a + 6;
      if a >= 8 then
      begin
        a := a - 8;
        x := b shr a;
        b := b mod (1 shl a);
        x := x mod 256;
        Result := Result + chr(x);
      end;
    end
    else
      Exit;
  end;
end;

KodeZwerg 5. Sep 2018 22:38

AW: Shop-Zugangsdaten verschlüsseln
 
Zitat:

Zitat von Schokohase (Beitrag 1412530)
Weil bei jedem Protect mit einem zusätzlichen (zufälligem) Salt verschlüsselt wird (macht die API von selber). Dadurch ist die Verschlüsselte Code-Folge immer unterschiedlich auch wenn die zu verschlüsselnden Daten gleich sind.

Wenn ich das richtig gesehen habe, hast Du bereits ein Overload eingebaut damit man selbst den Salt bestimmen kann, was ja einem Passwort recht nahe kommt, zumindest für den Lokal Angemeldeten User. Oder ich verstehe da etwas falsch (AOptionalEntropy).

Uwe Raabe 5. Sep 2018 22:52

AW: Shop-Zugangsdaten verschlüsseln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1412534)
Also für Base64 nehm ich nix was aus einer Delphi Unit kommt, da bläht sich Datei mit viel ungenutzten Indy methoden auf.

Das trifft aber seit einigen Delphi-Versionen nicht mehr zu. System.NetEncoding stellt Base64 recht einfach zur Verfügung und benötigt selbst auch nur System.Classes, System.SysUtils und System.RTLConsts - also Units, die vermutlich eh in jedem Programm schon eingebunden werden.

Schokohase 5. Sep 2018 23:31

AW: Shop-Zugangsdaten verschlüsseln
 
Zitat:

Zitat von KodeZwerg (Beitrag 1412535)
Zitat:

Zitat von Schokohase (Beitrag 1412530)
Weil bei jedem Protect mit einem zusätzlichen (zufälligem) Salt verschlüsselt wird (macht die API von selber). Dadurch ist die Verschlüsselte Code-Folge immer unterschiedlich auch wenn die zu verschlüsselnden Daten gleich sind.

Wenn ich das richtig gesehen habe, hast Du bereits ein Overload eingebaut damit man selbst den Salt bestimmen kann, was ja einem Passwort recht nahe kommt, zumindest für den Lokal Angemeldeten User. Oder ich verstehe da etwas falsch (AOptionalEntropy).

Ja, man kann damit Komplexität der Verschlüsselung zu vergrößern. Und ja, zum Entschlüsseln wird diese Bytefolge benötigt. Man könnte das durchaus im weitesten Sinne als Passwort verstehen.

Trotz allem bekommt man bei gleichen Eingangswerten beim Protect immer unterschiedliche Byte-Folgen zurück

Hobbycoder 8. Sep 2018 08:23

AW: Shop-Zugangsdaten verschlüsseln
 
Also bei mir klappt das wunderbar.

Man könnte das ganze noch zur vereinfachten Verwendung noch um ein paar Methoden erweitern, damit es jeder direkt verwenden kann.

Delphi-Quellcode:
uses
   System.SysUtils, System.NetEncoding;

type
  function ProtectData(data: string): TBytes;
  function UnprotectData(data: TBytes): string;
  function ProtectString(data: string): string;
  function UnprotectString(data: string): string;

implementation

function ProtectData(data: string): TBytes;
var
  inBuffer: TBytes;
begin
  inBuffer:=TEncoding.UTF8.GetBytes(data);
  Result:=TProtectedData.Protect(inBuffer);
end;

function UnprotectData(data: TBytes): string;
var
  outBuffer: TBytes;
begin
  outBuffer:=TProtectedData.Unprotect(data);
  Result:=TEncoding.UTF8.GetString(outBuffer)
end;

function ProtectString(data: string): string;
var
  ne: TNetEncoding;
  Buffer: TBytes;
begin
  ne:=TNetEncoding.Create;
  try
    Buffer:=ProtectData(data);
    Result:=ne.Base64.EncodeBytesToString(Buffer);
  finally
    ne.Free;
  end;
end;

function UnprotectString(data: string): string;
var
  ne: TNetEncoding;
  Buffer: TBytes;
begin
  ne:=TNetEncoding.Create;
  try
    Buffer:=ne.Base64.DecodeStringToBytes(data);
    Result:=UnprotectData(Buffer);
  finally
    ne.Free;
  end;
end;

end.
Und um diese Daten auch in INI-Datei zu speichern:

Delphi-Quellcode:
procedure WriteMultiLineStringToIni(ini: TIniFile; Section: string; Ident: string; Value: string);
var
  i: Integer;
  sl: TStringList;
begin
  sl:=TStringList.Create;
  try
    sl.Text:=Value;
    for i:=sl.Count-1 downto 0 do
      if sl[i]='' then sl.Delete(i);
    ini.WriteInteger(Section, Ident+'Count', sl.Count);
    for i:=0 to sl.Count-1 do
      ini.WriteString(Section, Ident+inttostr(i), sl[i]);
  finally
    sl.Free;
  end;
end;

function ReadMultilineStringFromIni(ini: TIniFile; Section: string; Ident: string): string;
var
  i, count: Integer;
  sl: TStringList;
begin
  sl:=TStringList.Create;
  try
    Result:='';
    if ini.SectionExists(Section) then
    begin
      if ini.ValueExists(Section, Ident+'Count') then
      begin
        count:=ini.ReadInteger(Section, Ident+'Count', 0);
        for i:=0 to Count-1 do
          sl.Add(ini.ReadString(Section, Ident+inttostr(i), ''));
        for i:=sl.Count-1 downto 0 do
          if sl[i]='' then sl.Delete(i);
        Result:=sl.Text;
      end;
    end;
  finally
    sl.Free;
  end;
end;

KodeZwerg 8. Sep 2018 11:31

AW: Shop-Zugangsdaten verschlüsseln
 
Zitat:

Zitat von Schokohase (Beitrag 1412538)
Zitat:

Zitat von KodeZwerg (Beitrag 1412535)
Zitat:

Zitat von Schokohase (Beitrag 1412530)
Weil bei jedem Protect mit einem zusätzlichen (zufälligem) Salt verschlüsselt wird (macht die API von selber). Dadurch ist die Verschlüsselte Code-Folge immer unterschiedlich auch wenn die zu verschlüsselnden Daten gleich sind.

Wenn ich das richtig gesehen habe, hast Du bereits ein Overload eingebaut damit man selbst den Salt bestimmen kann, was ja einem Passwort recht nahe kommt, zumindest für den Lokal Angemeldeten User. Oder ich verstehe da etwas falsch (AOptionalEntropy).

Ja, man kann damit Komplexität der Verschlüsselung zu vergrößern. Und ja, zum Entschlüsseln wird diese Bytefolge benötigt. Man könnte das durchaus im weitesten Sinne als Passwort verstehen.

Trotz allem bekommt man bei gleichen Eingangswerten beim Protect immer unterschiedliche Byte-Folgen zurück

Da hatte ich doch glatt vergessen Danke zu Sagen. Das man einen Wert nicht hardcoden sollte, dessen war ich mir bewusst. Ein entschlüsseln sollte hingegen immer funktionieren, egal aus welcher unique variation generation es entstammt. Das AOptionalEntropy kann man Hardcoden aber ob es da noch einen Unterschied macht, das kodierte ist ja bereits auf eine Maschiene/Benutzerkonto limitiert, zumindest gäbe es damit noch ein fünkchen mehr Sicherheit/Einfluss. Ich finde es eine gute Windows dreingabe, die man dank Dir nun einfach nutzen kann. :thumb:

Codehunter 10. Sep 2018 07:57

AW: Shop-Zugangsdaten verschlüsseln
 
Ich habe hier mit Freude mitgelesen und muss sagen, so einfach und verständlich wurde dieses Thema hier noch nie abgehandelt. Ich hatte vor Jahren hier das selbe aufgeworfen. Da wurde es sehr abstrakt. Im Grunde habt ihr jetzt 5 Jahre später die Lösung auf dem Silbertablett serviert :-)

freimatz 10. Sep 2018 09:57

AW: Shop-Zugangsdaten verschlüsseln
 
Auch von mir wieder ein Danke.
In der Zwischenzeit habe ich die unit-tests verlassen und bin dabei das im Produktiv-Code zu verwenden. Für Base64 verwende ich nun auch TNetEncoding. Allerdings entferne ich alle Zeilenumbrüche.


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:37 Uhr.

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