Autor Téma: Šifrování souborů  (Přečteno 520 krát)

Offline rmaster

  • Nováček
  • *
  • Příspěvků: 12
  • Karma: 0
    • Verze Delphi: XE3
Šifrování souborů
« kdy: 15-02-2025, 15:59:29 »
Zdravím, potřebuji zdánlivě jednoduchou věc, nad kterou už bádám druhý den a jsem v koncích.
Chci na serveru v PHP zašifrovat textový soubor, který předem vygeneruji, pak ho přenést na Windows (to nyní neřeším) a ten soubor následně načíst mojí aplikací a rozšifrovat. Používám Delphi XE3.
Mělo by se jednat o offline registraci mého programu, když uživatel nemá přístup k internetu.
Vše mi funguje, ale obsah zdrojového soubor před zašifrováním a obsah cílového souboru po dešifrování je úplně jiný.
Nedokázal by někdo poradit? Moc Děkuji.
Kód: Delphi [Vybrat]
  1. <?php
  2. $inputFile = 'soubor.txt';
  3. $outputFile = 'soubor_encrypted.txt';
  4.  
  5. $key = 'tajny_klic_12345';  
  6. $iv = openssl_random_pseudo_bytes(16); // Inicializační vektor
  7.  
  8. $data = file_get_contents($inputFile);
  9.  
  10. $ciphertext = openssl_encrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
  11.  
  12. $ivAndCiphertext = $iv . $ciphertext;
  13.  
  14. $base64EncodedData = base64_encode($ivAndCiphertext);
  15.  
  16. file_put_contents($outputFile, $base64EncodedData);
  17.  
  18. echo "Soubor byl úspěšně zašifrován a uložen ve formátu Base64.";
  19. ?>
Kód: Delphi [Vybrat]
  1. uses
  2.   System.SysUtils, System.Classes, DCPrijndael, DCPcrypt2;
  3.  
  4. const
  5.   Base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  6.  
  7. function DecodeBase64(const Input: string): TBytes;
  8. var
  9.   i, j, Triplet: Integer;
  10.   OutputIndex: Integer;
  11.   PadCount: Integer;
  12. begin
  13.   PadCount := 0;
  14.   for i := Length(Input) downto 1 do
  15.   begin
  16.     if Input[i] = '=' then
  17.       Inc(PadCount)
  18.     else
  19.       Break;
  20.   end;
  21.  
  22.   SetLength(Result, (Length(Input) * 3) div 4 - PadCount);
  23.   OutputIndex := 0;
  24.  
  25.   for i := 1 to Length(Input) div 4 do
  26.   begin
  27.     Triplet := (Pos(Input[(i - 1) * 4 + 1], Base64Chars) - 1) shl 18 or
  28.                (Pos(Input[(i - 1) * 4 + 2], Base64Chars) - 1) shl 12 or
  29.                (Pos(Input[(i - 1) * 4 + 3], Base64Chars) - 1) shl 6 or
  30.                (Pos(Input[(i - 1) * 4 + 4], Base64Chars) - 1);
  31.  
  32.     Result[OutputIndex] := (Triplet shr 16) and $FF;
  33.     Result[OutputIndex + 1] := (Triplet shr 8) and $FF;
  34.     Result[OutputIndex + 2] := Triplet and $FF;
  35.  
  36.     OutputIndex := OutputIndex + 3;
  37.   end;
  38.  
  39.   if PadCount = 1 then
  40.     SetLength(Result, Length(Result) - 1);
  41.   if PadCount = 2 then
  42.     SetLength(Result, Length(Result) - 2);
  43. end;
  44. procedure DecryptFile(const EncryptedFile, DecryptedFile, Key: string);
  45. var
  46.   InputStream, OutputStream: TFileStream;
  47.   Base64EncodedData: string;
  48.   IVAndCiphertext, IV, EncryptedData: TBytes;
  49.   Rijndael: TDCP_rijndael;
  50. begin
  51.   InputStream := TFileStream.Create(EncryptedFile, fmOpenRead);
  52.   try
  53.     SetLength(Base64EncodedData, InputStream.Size);
  54.     InputStream.ReadBuffer(Base64EncodedData[1], InputStream.Size);
  55.  
  56.     IVAndCiphertext := DecodeBase64(Base64EncodedData);
  57.  
  58.     SetLength(IV, 16);
  59.     Move(IVAndCiphertext[0], IV[0], 16);
  60.  
  61.     SetLength(EncryptedData, Length(IVAndCiphertext) - 16);
  62.     Move(IVAndCiphertext[16], EncryptedData[0], Length(EncryptedData));
  63.  
  64.     Rijndael := TDCP_rijndael.Create(nil);
  65.     try
  66.       Rijndael.Init(Key[1], 256, IV);  // AES-256, klíč o délce 256 bitů
  67.  
  68.       Rijndael.Decrypt(EncryptedData[0], EncryptedData[0], Length(EncryptedData));
  69.  
  70.       OutputStream := TFileStream.Create(DecryptedFile, fmCreate);
  71.       try
  72.         OutputStream.WriteBuffer(EncryptedData[0], Length(EncryptedData));
  73.       finally
  74.         OutputStream.Free;
  75.       end;
  76.     finally
  77.       Rijndael.Free;
  78.     end;
  79.   finally
  80.     InputStream.Free;
  81.   end;
  82. end;
  83. ..........................
  84. // Použití
  85. begin
  86.   // Zavoláme funkci pro dešifrování
  87.   DecryptFile('soubor_encrypted.txt', 'soubor_decrypted.txt', 'tajny_klic_12345');
  88.  
  89.   // Výsledek je uložen do souboru 'soubor_decrypted.txt'
  90.   ShowMessage('Soubor byl úspěšně dešifrován a uložen.');
  91. end.
  92.  
  93.  

Offline Jiří Bílý

  • Nováček
  • *
  • Příspěvků: 3
  • Karma: 0
    • Verze Delphi: 11.3
Re:Šifrování souborů
« Odpověď #1 kdy: 15-02-2025, 16:56:03 »
Zkontroluj si, že máš při šifrování i dešifrování stejnou hodnotu inicializačního vektoru (IV) a jestli při dešifrování používáš mód CBC.
Něco o tom viz. např. https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

Offline Jan Fiala

  • Hrdina
  • ****
  • Příspěvků: 453
  • Karma: 6
    • Verze Delphi: 10.4.1
    • PSPad editor
Re:Šifrování souborů
« Odpověď #2 kdy: 15-02-2025, 20:06:27 »
Vyzkousej si to postupne. Base64 encode/decode oboustranne bez sifrovani funguje?

Offline erik

  • Nováček
  • *
  • Příspěvků: 6
  • Karma: 0
    • Verze Delphi: Delphi 7 - Delphi 12
Re:Šifrování souborů
« Odpověď #3 kdy: 15-02-2025, 23:09:39 »
1. Skontroluj kodovanie suboru. Ak je to ASCII (cto je zrejme cielom Base64 kodovania),
tak potom premenna Base64EncodedData ma byt zrejme typu AnsiString a takisto Input parameter v DecodeBase64.
2. Ak Delphi XE3 obsahuje triedu TBase64Encoding, tak by som ju pouzil namiesto vlastnej implementacie.
3. Takisto namiesto "nejakej" AES implementacie by som pouzil radsej zabudovane Windows AES sifrovanie (BCrypt).
4. Zbytocne sa tam data kopiruju. Mozu sa desifrovat rovno z IVAndCiphertext pola po preskoceni 16bajtoveho IV. Na druhej strane treba skontrolovat, ci pri desifrovani moze byt vstupny aj vystupny buffer rovnaky.

Offline rmaster

  • Nováček
  • *
  • Příspěvků: 12
  • Karma: 0
    • Verze Delphi: XE3
Re:Šifrování souborů
« Odpověď #4 kdy: 17-02-2025, 14:58:55 »
Pánové, děkuji moc za nasměrování. Podařilo se mi postupný textováním dojít k tomu, že klíč musí být vždy 256 a tak nakonec stačilo přidat v PHP toto:
Kód: Delphi [Vybrat]
  1. $key = hash('sha256', $key, true);

Teď už to funguje, ale ještě se mi na konci výsledného souboru přidávají nějaké dodatečné znaky (2-5 - podle velikosti dat).
A to nevím, kde se tam bere. Převod Base64 jsem nakonec použil z INDY.

Finální verze je takto:
 
Kód: Delphi [Vybrat]
  1. function DecryptBytes(const EncryptedData: TBytes; const Key: string; const IV: TBytes): TBytes;
  2. var
  3.   Cipher: TDCP_rijndael;
  4.   Hash: TDCP_sha256;
  5.   KeyBytes: TBytes;
  6. begin
  7.   Hash := TDCP_sha256.Create(nil);
  8.   try
  9.     SetLength(KeyBytes, 32);  
  10.     Hash.Init;
  11.     Hash.UpdateStr(Key);      
  12.     Hash.Final(KeyBytes[0]);  
  13.  
  14.     Cipher := TDCP_rijndael.Create(nil);
  15.     try
  16.       Cipher.CipherMode := cmCBC;
  17.       Cipher.Init(KeyBytes[0], 256, @IV[0]);
  18.  
  19.       SetLength(Result, Length(EncryptedData));
  20.       Cipher.DecryptCBC(EncryptedData[0], Result[0], Length(EncryptedData));
  21.  
  22.     finally
  23.       Cipher.Burn;  
  24.       Cipher.Free;
  25.     end;
  26.   finally
  27.     Hash.Free;
  28.   end;
  29. end;
  30.  
  31.  
  32. procedure TForm1.DecryptFile(const EncryptedFile, Heslo: string);
  33. var
  34.   SL: TStringList;
  35.   Base64EncodedData: AnsiString;
  36.   IVAndCiphertext, IV, EncryptedData, EncryptedData2: TBytes;
  37.   FileStream: TFileStream;
  38.   MujText: String;
  39. begin
  40.  
  41.   SL := TStringList.Create;
  42.   try
  43.     SL.LoadFromFile(EncryptedFile, TEncoding.ASCII);    
  44.     Base64EncodedData := SL.Text;
  45.   finally
  46.     SL.Free;
  47.   end;
  48.  
  49.   Memo1.Lines.Add('Zašifrovaný text: ' + Base64EncodedData);
  50.  
  51.   IVAndCiphertext := TIdDecoderMIME.DecodeBytes(Base64EncodedData);                    
  52.  
  53.   SetLength(IV, 16);
  54.   Move(IVAndCiphertext[0], IV[0], 16);
  55.  
  56.   SetLength(EncryptedData, Length(IVAndCiphertext) - 16);
  57.   Move(IVAndCiphertext[16], EncryptedData[0], Length(EncryptedData));
  58.  
  59.   EncryptedData2:=DecryptBytes(EncryptedData, Heslo, IV);
  60.  
  61.   FileStream := TFileStream.Create('soubor_decrypted_delphi.txt', fmCreate);
  62.   try
  63.     FileStream.Write(EncryptedData2[0], Length(EncryptedData2));
  64.   finally
  65.     FileStream.Free;
  66.   end;
  67.  
  68.   MujText := TEncoding.UTF8.GetString(EncryptedData2);
  69.   Memo1.Lines.Add('Dešifrovaný text: -' +MujText+'-');
  70.  
  71. end;
  72.  

Díky

Offline rmaster

  • Nováček
  • *
  • Příspěvků: 12
  • Karma: 0
    • Verze Delphi: XE3
Re:Šifrování souborů
« Odpověď #5 kdy: 17-02-2025, 21:41:07 »
Tak už jsem na to přišel.
Zdá se, že problém byl způsoben paddingem při šifrování a dešifrování. V PHP openssl_encrypt() automaticky přidává padding (PKCS#7), zatímco knihovna DCPcrypt v Delphi (TDCP_rijndael) nepracuje s paddingem automaticky a musí se na konci odebrat.
Díky za nasměrování.