![]() |
Shop-Zugangsdaten verschlüsseln
Hallo zusammen,
ich habe eine ähnliche Problematik wie bei ![]() 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: ![]() 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? |
AW: Shop-Zugangsdaten verschlüsseln
Du kannst dafür die
![]() |
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:
|
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:
Hier noch etwas auf ![]() |
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. |
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:
in
PVOID
Delphi-Quellcode:
Pointer
Änderung #2:
Delphi-Quellcode:
in
Entropy := [];
Delphi-Quellcode:
SetLength(Entropy, 0);
Danke für Mühe, ich kannte das noch nicht. |
AW: Shop-Zugangsdaten verschlüsseln
Geschrieben habe ich das unter 10.2.3.
Da befindet sich in der Winapi.Windows
Delphi-Quellcode:
Und genau diesen Eintrag würde ich an deiner Stelle in der Unit hinzufügen anstatt in der Deklaration
PVOID = Pointer;
{$EXTERNALSYM PVOID}
Delphi-Quellcode:
durch
PVOID
Delphi-Quellcode:
zu ersetzen. Dann sieht es exakt so aus wie in der API-Dokumentation.
Pointer
Delphi-Quellcode:
kann man ab Delphi Version X verwenden und ist das gleiche wie
Entropy = [];
Delphi-Quellcode:
SetLength(Entropy,0);
|
AW: Shop-Zugangsdaten verschlüsseln
Dann ist es also Okay für mein Delphi, vielen Dank für Antwort!:thumb:
edit Zitat:
|
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) |
AW: Shop-Zugangsdaten verschlüsseln
Zitat:
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. |
AW: Shop-Zugangsdaten verschlüsseln
Danke.
|
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; |
AW: Shop-Zugangsdaten verschlüsseln
*entfernt, ich verstehe TE gerade nicht*
|
AW: Shop-Zugangsdaten verschlüsseln
Tja, Sorry Kathinka, das war für die Katz ;-)
Zitat:
Zitat:
|
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:
Dazu habe ich einen unit-test gemacht:
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;
Delphi-Quellcode:
Der läuft gut. Anders sieht es mit dem nächsten aus.
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;
Delphi-Quellcode:
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.
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; Wo ist mein Denkfehler? |
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.
|
AW: Shop-Zugangsdaten verschlüsseln
Zitat:
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; |
AW: Shop-Zugangsdaten verschlüsseln
Zitat:
|
AW: Shop-Zugangsdaten verschlüsseln
Zitat:
|
AW: Shop-Zugangsdaten verschlüsseln
Zitat:
Trotz allem bekommt man bei gleichen Eingangswerten beim Protect immer unterschiedliche Byte-Folgen zurück |
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:
Und um diese Daten auch in INI-Datei zu speichern:
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.
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; |
AW: Shop-Zugangsdaten verschlüsseln
Zitat:
|
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
![]() |
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