Ajakirjateemast projektipraktikasse
Sobivad teenuse- ja tehnilised lehed postituse jaoks
Praktiliselt ei ebaõnnestu AES Verschlüsselung Delphi harva „ise AES-i“ tõttu, vaid tingimuste tõttu: andmeid tuleb töödelda voona (failid, BLOB-id, varukoopiad), vanu formaate peab suutma edasi lugeda ning tootmises on vaja silumistõendatavust (päised, versioonihaldus) ja turvalisi vaikeseadeid (salt/IV juhuslik, mitte taaskasutatav). See lähtekoodilõik näitab seetõttu mitte ainult „Encrypt/Decrypt“, vaid väikest, vastupidavat formaati päise, versiooni, salti ja IV-iga – pluss PBKDF2 võtmeeralduseks ning koht, kuhu integriteet mõistlikult lisada.
Warum „AES-String verschlüsseln“ fast nie reicht
Erinevas ettevõtte tarkvaras esineb krüpteerimine tavaliselt kolmes kohas: (1) konfiguratsioon/secrets (nt ligipääsuandmed), (2) vahetus-/eksportfailid ja (3) puhkeolekus olevad andmed (nt arhiivid, dokumendikonteinerid). Naiivne lähenemine „parool → AES-võti → string sisse/välja“ kukub kiiresti läbi:
- IV-Wiederverwendung: režiimide nagu CBC või GCM puhul peab initsialiseerimisvektor (IV) olema iga krüptimise kohta unikaalne. Konstantne IV on lekkekoht, isegi kui parool on tugev.
- Key aus Passwort ohne KDF: parooli otsene kasutamine võtmena (või ühekordne hashimine) avab ukse offline-rünnakutele. KDF (Key Derivation Function) nagu PBKDF2 aeglustab ründajat sihipäraselt.
- Keine Formatversion: ilma päise/versioonita on iteratsioonide arvu, algoritmi või parameetrite muutmine hiljem keeruline, nii et vanad andmed võivad „hüljatud“ jääda.
- Keine Integrität: AES-CBC krüpteerib, kuid ei kaitse manipuleerimise eest. Ilma autentimiseta (nt HMAC või AEAD nagu GCM) saate bitflipping-/padding-probleeme ja raskesti diagnoositavaid veamustreid.
Sellest artikli tuum: väike konteineriformaat, mis toetab vootöötlust, on versioonitav ja väldib tavalisi vigu.
AES Verschlüsselung Delphi mit Header, Salt, IV und PBKDF2
Me määratleme lihtsa konteineriformaadi, mida saab kasutada ka andmebaasi BLOB-ides või sõnumi-payloadides:
- Magic: 4 Bytes, z. B.
NBAE(schneller „Ist das unser Format?“-Check) - Version: 1 Byte (ermöglicht Migration)
- KDF-Parameter: Iterationszahl (4 Bytes)
- Salt: 16 Bytes (zufällig pro Datei)
- IV: 16 Bytes (zufällig pro Datei für AES-CBC)
- Ciphertext: verschlüsselte Nutzdaten (streamingfähig)
Tähtis: salt ja IV ei ole saladus. Need peavad iga krüptimise kohta uued olema. Parool jääb salajaseks; sellest tuletatud võtit ei salvestata.
AES Verschlüsselung Delphi im Stream: Container schreiben/lesen
Kood on teadlikult kirja pandud kui „ehitusplaan“: selgelt eraldatud funktsioonid, kontrollitavad päised, pole peidetud globaale. AES-i ja PBKDF2 jaoks kasutavad paljud meeskonnad tõestatud krüpto-raamatukogu (nt DEC). Lõik demonstreerib formaati ja vootöötluse mustrit; AES-/PBKDF2-kutsed on nii kapseldatud, et saate need vastavalt raamatukogule välja vahetada.
unit Nb.AesContainer;
interface
uses
System.SysUtils, System.Classes, System.NetEncoding;
type
ENbCryptoError = class(Exception);
TNbAesContainer = class
public
class procedure EncryptStreamToStream(const AIn: TStream; const AOut: TStream;
const APassword: string; const AIterations: Cardinal = 200000);
class procedure DecryptStreamToStream(const AIn: TStream; const AOut: TStream;
const APassword: string);
class function EncryptBytesToBase64(const APlain: TBytes; const APassword: string): string;
class function DecryptBase64ToBytes(const ACipherB64: string; const APassword: string): TBytes;
end;
implementation
const
CMagic: array[0..3] of AnsiChar = ('N','B','A','E');
CVersion: Byte = 1;
CSaltLen = 16;
CIvLen = 16;
type
TNbHeaderV1 = packed record
Magic: array[0..3] of AnsiChar;
Version: Byte;
Iterations: Cardinal; // little endian
Salt: array[0..CSaltLen-1] of Byte;
IV: array[0..CIvLen-1] of Byte;
end;
// --- Sõltuvused, mida tuleb realiseerida sõltuvalt krüptopiinist ---
procedure FillRandomBytes(var B: TBytes);
begin
// Krüptograafilise juhuslikkuse jaoks kasutage OS CSPRNG-i (Windows BCryptGenRandom,
// Linux getrandom/urandom). Siin on teadlikult kohatäide.
raise ENbCryptoError.Create('FillRandomBytes: CSPRNG ei ole seotud');
end;
function PBKDF2_HMAC_SHA256(const APassword: string; const ASalt: TBytes;
const AIterations, AKeyLen: Cardinal): TBytes;
begin
// Rakendus näiteks DEC-iga (PBKDF2) või muu teegiga.
// Tulemus: AKeyLen baiti.
raise ENbCryptoError.Create('PBKDF2_HMAC_SHA256: ei ole seotud');
end;
procedure AES256_CBC_EncryptStream(const AKey, AIV: TBytes; const AIn, AOut: TStream);
begin
// Rakendus teegiga:
// - KeyLen = 32 Bytes
// - IVLen = 16 Bytes
// - PKCS#7 Padding
// Tähtis: töödelda vooguna, mitte kogu mällu lugeda.
raise ENbCryptoError.Create('AES256_CBC_EncryptStream: ei ole seotud');
end;
procedure AES256_CBC_DecryptStream(const AKey, AIV: TBytes; const AIn, AOut: TStream);
begin
raise ENbCryptoError.Create('AES256_CBC_DecryptStream: ei ole seotud');
end;
// --- Abimeetodid ---
procedure WriteHeaderV1(const AOut: TStream; const H: TNbHeaderV1);
begin
if AOut.Write(H, SizeOf(H)) <> SizeOf(H) then
raise ENbCryptoError.Create('Headerit ei õnnestunud kirjutada');
end;
function ReadHeaderV1(const AIn: TStream): TNbHeaderV1;
var
H: TNbHeaderV1;
begin
if AIn.Read(H, SizeOf(H)) <> SizeOf(H) then
raise ENbCryptoError.Create('Header puudulik');
if (H.Magic[0] <> CMagic[0]) or (H.Magic[1] <> CMagic[1]) or
(H.Magic[2] <> CMagic[2]) or (H.Magic[3] <> CMagic[3]) then
raise ENbCryptoError.Create('Kehtetu konteiner (Magic ei klapi)');
if H.Version <> CVersion then
raise ENbCryptoError.CreateFmt('Tundmatu konteineri versioon: %d', [H.Version]);
if (H.Iterations < 10000) or (H.Iterations > 5000000) then
raise ENbCryptoError.Create('Iteratsioonide arv on mõistlikest piiridest väljas');
Result := H;
end;
class procedure TNbAesContainer.EncryptStreamToStream(const AIn, AOut: TStream;
const APassword: string; const AIterations: Cardinal);
var
H: TNbHeaderV1;
Salt, IV, Key: TBytes;
begin
if APassword = '' then
raise ENbCryptoError.Create('Parool ei tohi olla tühi');
// Salt/IV genereerimine
SetLength(Salt, CSaltLen);
SetLength(IV, CIvLen);
FillRandomBytes(Salt);
FillRandomBytes(IV);
// Päise täitmine
Move(CMagic[0], H.Magic[0], Length(CMagic));
H.Version := CVersion;
H.Iterations := AIterations;
Move(Salt[0], H.Salt[0], CSaltLen);
Move(IV[0], H.IV[0], CIvLen);
WriteHeaderV1(AOut, H);
// Võtme tuletamine (32 baiti AES-256 jaoks)
Key := PBKDF2_HMAC_SHA256(APassword, Salt, AIterations, 32);
// Andmete krüpteerimine (kriptotekst järgneb otse päise järel)
AES256_CBC_EncryptStream(Key, IV, AIn, AOut);
end;
class procedure TNbAesContainer.DecryptStreamToStream(const AIn, AOut: TStream;
const APassword: string);
var
H: TNbHeaderV1;
Salt, IV, Key: TBytes;
begin
if APassword = '' then
raise ENbCryptoError.Create('Parool ei tohi olla tühi');
H := ReadHeaderV1(AIn);
SetLength(Salt, CSaltLen);
SetLength(IV, CIvLen);
Move(H.Salt[0], Salt[0], CSaltLen);
Move(H.IV[0], IV[0], CIvLen);
Key := PBKDF2_HMAC_SHA256(APassword, Salt, H.Iterations, 32);
// Dekrüpteerimine alates praegusest voogi positsioonist (pärast päist)
AES256_CBC_DecryptStream(Key, IV, AIn, AOut);
end;
class function TNbAesContainer.EncryptBytesToBase64(const APlain: TBytes;
const APassword: string): string;
var
InS, OutS: TBytesStream;
begin
InS := TBytesStream.Create(APlain);
try
OutS := TBytesStream.Create;
try
EncryptStreamToStream(InS, OutS, APassword);
Result := TNetEncoding.Base64.EncodeBytesToString(OutS.Bytes, 0, OutS.Size);
finally
OutS.Free;
end;
finally
InS.Free;
end;
end;
class function TNbAesContainer.DecryptBase64ToBytes(const ACipherB64,
APassword: string): TBytes;
var
Cipher: TBytes;
InS, OutS: TBytesStream;
begin
Cipher := TNetEncoding.Base64.DecodeStringToBytes(ACipherB64);
InS := TBytesStream.Create(Cipher);
try
OutS := TBytesStream.Create;
try
DecryptStreamToStream(InS, OutS, APassword);
Result := OutS.Bytes;
SetLength(Result, OutS.Size);
finally
OutS.Free;
end;
finally
InS.Free;
end;
end;
end.Eesmärk: Minimaalne konteiner, mis sobib failide ja BLOB-ide hoidmiseks, sealhulgas versioonihaldus ja KDF-parameetrid. Raamtingimused: Paigutage alla tõeline CSPRNG-liides (operatsioonisüsteemi poolt pakutav krüptograafiliselt turvaline juhuslikkus) ja robustne AES/PBKDF2-implementatsioon. Lõksud: Ärge kasutage „mingit“ Random-i (mitte Random()), ärge kasutage fikseeritud IV-e ning planeerige decrypt-imisel selge veakäsitlus (vale parool vs. rikutud andmed). Variandid: CBC asemel eelistada AEAD-i (vt allpool) või laiendada päist algoritmi-ID ja HMAC-iga.
Terviklikkus: miks AES-CBC üksi tootmises riskantne on
AES-CBC esineb paljudes pärandkontekstides ja võib toimida, kui kasutate lisaks terviklikkuse kontrolli. Ilma terviklikkuseta saab ründaja šifriteksti manipuleerida; isegi ilma aktiivse ründajata võivad edastusvead või rikutud salvestuskihid põhjustada raskesti diagnoositavaid „Padding“-vigu.
Praktilised valikud:
- Encrypt-then-HMAC: Pärast šifriteksti kirjutage HMAC (nt HMAC-SHA-256) üle päise+šifriteksti. Lugemisel kontrollige esmalt HMAC-i ja alles seejärel dekrüpteerige. Selleks tuletage ideaalis kaks võtit PBKDF2-st (nt 64 baiti: 32 AES-iks, 32 HMAC-iks) selle asemel, et sama võtit kahe eesmärgi jaoks taaskasutada.
- AES-GCM: AEAD-režiim (Authenticated Encryption with Associated Data). Tagab šifriteksti koos tõendusmärgiga (auth-tag). See on tänapäeval sageli kõige selgem valik, kui teie Delphi-raamatukogu toetab GCM-i stabiilselt. Päise väljad saab autentida kui „AAD“, ilma et neid peaks krüpteerima.
Kui peate CBC-sse jääma (nt ühilduvuse tõttu), on Encrypt-then-HMAC tugev täiendus. Uute formaatide puhul on GCM sageli otstarbekas, sest see annab autentimise „kaasas“ ja veapildid on selgemad.
Ebatavaliselt oluline: „Krüptograafiline juhuslikkus“ ja miks System.Hash ei piisa
Üks levinud pärandirefleks Delphi-projektides on: „Me võtame lihtsalt SHA256 ajatempli + midagi ja arvame, et meil on Random.“ See ei ole usaldusväärne alus. Salt-i ja IV-i jaoks vajate operatsioonisüsteemi CSPRNG-i (Cryptographically Secure Pseudo Random Number Generator). Under Windows on see tavaliselt BCrypt-API (CNG), under Linux kernel-generaator nagu getrandom() või /dev/urandom. Tähtis erinevus on praktiline: CSPRNG on disainitud nii, et vaadeldud väärtustest ei saa ennustada järgnevate väärtuste järjestust.
Arhitektuuriline nipp: kapseldage see väikesesse „RandomProvider“-unit-i, mida saate testides mockida. See lahendab kaks olukorda korraga: reproduutseeritavad testid (fikseeritud siem mock-is) ja reaalne turvalisus tootmises (OS-CSPRNG). Nii väldite, et kiire remondi korral rändab süsteemi taas sisse Random(), kuna see tundub lihtne ja kiire lahendus.
Debugimine ja pärandmigratsioon: versioonihaldus ei ole luksus
Päis ei ole tähtis ainult „krüpto-esteetika“ pärast, vaid hooldatavuse huvides:
- Iteration Tuning: PBKDF2-iteratsioonide arv muutub aastatega. Päise välja abil saate hiljem iteratsioone tõsta ilma vanu andmeid loetamatuks muutmata.
- Formatimuutused: Versioon 2 võib näiteks üle minna AES-GCM-ile või lisada HMAC-i.
- Välja diagnoosimine: Magic/Version võimaldavad kiireid kontrolle logides ja tööriistades ilma andmeid dekrüpteerimata.
Praxis-Tipp: Implementieren Sie einen kleinen „Inspector“, der nur den Header ausliest (Magic/Version/Iterations) und in ein Log schreibt. Damit klären Sie viele Supportfälle („Welche Version liegt hier?“) ohne Passwort-Handling.
Puhas migratsioon: „Read old, write new“ Big Bang’i asemel
Kui asendate vana formaati (nt fikseeritud IV, puuduv KDF, Blowfish/3DES või ise tehtud XOR), on Delphi-projektides end ära tõestanud muster järgmine: beim lugemisel tuvastate mitu formaati (Magic/Version või Fallback-heuristik), beim kirjutamisel toodate ainult uue formaadi. Lisaks võite edukal dešifreerimisel taustal uuesti krüpteerida („lazy migration“), kui see protsessiga sobib. Nii vähendate rollout-riski ja väldite „einmal alles neu verschlüsseln“ kui hooldusakent.
Lõimimine ja voogedastus: tüüpilised kitsaskohad Delphi
Krüpteerimine jookseb sageli töötluslõimedes (nt eksportimisel, üleslaadimisel Kundenportal, suurte arhiivide kirjutamisel). Kaks punkti, mis Delphi-projektides regulaarselt esile kerkivad:
- Stream-Positionen: Enne krüpteerimist/dešifreerimist määrake selged kokkulepped: sisendvoog loetakse alates praegusest positsioonist, väljundvoog kirjutatakse alates praegusest positsioonist. Voogude taaskasutamisel veenduge, et
Position := 0on teadlikult seatud. - Memory-Spitzen: Vältige „alles in TBytes“. Voopõhine lähenemine on eriti oluline suurte failide puhul. Kui teie Crypto-Bibliothek aktsepteerib ainult Byte-Arrays, tasub teha lisatööd ja üle minna streamfähige implementatsioonile või ehitada puhverdav adapter.
Kui krüpteerite teenustes (Windows- või Linux-Services), pöörake tähelepanu korrektsesse erandilogimisse: „vale Passwort“, „Header kaputt“, „Tag/HMAC ungültig“ on erinevad operatsioonijuhud ja peaksid olema eristatavad. Oluline: veateated ei tohi väliskliendile olla liiga detailsed (mitte „Padding falsch in Block 7“ kui API-viga), siselogis võivad need detailid aga olemas olla.
Millal lähenemisviis tasub end ära – ja kus see võib ebaõnnestuda
Lohnt sich, kui te: (a) salvestate verschlüsselte Export-/Importdaten pikaajaliselt, (b) käitate erinevaid Programmversionen paralleelselt, (c) töötlete Daten als Streams või (d) vajate puhast Krypto-Schnittstelle mitmele Module (Client/Server/Tooling).
Kippt, kui proovite sellega „alles“ lahendada: für Transport vastutab TLS, mitte ise ehitatud AES-Wrapper. Für Secrets (Passwörter, Tokens) on sageli mõistlikum kasutada OS-Secret-Store’i või Vault’i. Ja kui vajate Interop mit anderen Sprachen, peate Header, Endianness und Encoding täpselt dokumenteerima (või kasutama etablierte Format).
Kokkuvõte: AES in Delphi on vähem Algorithmus, rohkem Engineering
Selle lõigu tegelik kasu ei ole „AES läuft“, vaid töökindel formaat: juhuslik Salt und IV, versionierter Header, PBKDF2-Parameter im Payload und streamfähige Verarbeitung. Lisage uutele formaatidele võimalusel integriteet (AES-GCM või Encrypt-then-HMAC). Nii muutub „wir verschlüsseln irgendwas“ komponendiks, mis on digitaalsetes Unternehmenslösungen ka aastate pärast hooldatav ja migreeritav.
Kui peate sellise konteineri integreerima väljakujunenud Delphi-maastikku või puhtalt migreerima Legacy-Formatist, tasub läbi viia lühike arhitektuurikontroll (võtmehaldus, formaadiversioonid, käitamine/logimine). Täpsed üksikasjad lepime vajadusel meelsasti kokku vestluses:
Erialases kontekstis mängivad samuti olulist rolli Delphi AES ja PBKDF2 Delphi, kui integratsioonid, andmevood ja edasine arendus peavad korrektselt koos töötama.
Projekti või moderniseerimisettevõtmise arutamine koos Net-Base.
Järgmine samm
Kui teema muutub reaalseks projektiks, tuleks arhitektuuri, olemasolevat süsteemi ja käitust varakult ühiselt vaadelda.
Me ei toeta ainult üksikute küsimuste lahendamist, vaid ka siis, kui lähtekoodilõikudest, pärandsüsteemidest või portaalikontseptsioonidest peab saama usaldusväärne ettevõtteprojekt.
- Olemasolev olukord, sihtpilt ja tehnilised riskid hinnatakse üheskoos.
- REST, andmete juurdepääs, portaalid ja juurutamine ei lükata hilisemaks.
- Te näete varakult, milline tee on majanduslikult ja operatiivselt jätkusuutlik.