Scenarij 7.3: Šifriranje datoteka

Kategorija: Šifriranje
Složenost: ⭐⭐⭐ (Srednja)
Preduvjeti: Ključni materijal ili lozinka
Procijenjeno vrijeme: 15-20 minuta


Opis

Ovaj scenarij opisuje sigurno šifriranje datoteka post-kvantno sigurnim postupcima. Implementacija podržava šifriranje temeljeno na lozinci i šifriranje temeljeno na ključevima.

Slučajevi korištenja:

  • Šifriranje sigurnosnih kopija
  • Zaštita dokumenata
  • Konfiguracijske datoteke
  • Arhiviranje

Tijek rada

flowchart TD FILE[Originalna datoteka] --> COMPRESS[Opcionalno: komprimiranje] COMPRESS --> CHUNK[Podjela na dijelove] CHUNK --> ENCRYPT[AES-256-GCM po dijelu] subgraph Derivacija ključa PWD[Lozinka] --> KDF[Argon2id] KDF --> KEY[Ključ za šifriranje] end KEY --> ENCRYPT ENCRYPT --> OUTPUT[Šifrirana datoteka] style KEY fill:#e8f5e9 style OUTPUT fill:#e3f2fd


Primjer koda: Šifriranje datoteke

using WvdS.Security.Cryptography.X509Certificates.Extensions.PQ;
using System.Security.Cryptography;
 
using var ctx = PqCryptoContext.Initialize();
 
// Izvorna datoteka
string inputFile = "document.pdf";
string outputFile = "document.pdf.enc";
string password = "MySecurePassword123!";
 
// Parametri šifriranja
var salt = RandomNumberGenerator.GetBytes(32);
var nonce = RandomNumberGenerator.GetBytes(12);
 
// Derivacija ključa iz lozinke (Argon2id)
var key = ctx.DeriveKeyArgon2id(
    password: Encoding.UTF8.GetBytes(password),
    salt: salt,
    outputLength: 32,
    iterations: 3,
    memoryKiB: 65536,  // 64 MB
    parallelism: 4
);
 
// Čitanje datoteke
var plaintext = File.ReadAllBytes(inputFile);
 
// Šifriranje s AES-256-GCM
var ciphertext = new byte[plaintext.Length];
var tag = new byte[16];
 
using var aes = new OpenSslAesGcm(key);
aes.Encrypt(nonce, plaintext, ciphertext, tag);
 
// Zapisivanje zaglavlja + ciphertexta
using var output = File.Create(outputFile);
using var writer = new BinaryWriter(output);
 
// Magic number
writer.Write(Encoding.ASCII.GetBytes("PQENC"));
// Verzija
writer.Write((byte)1);
// Salt
writer.Write(salt.Length);
writer.Write(salt);
// Nonce
writer.Write(nonce.Length);
writer.Write(nonce);
// Tag
writer.Write(tag.Length);
writer.Write(tag);
// Ciphertext
writer.Write(ciphertext.Length);
writer.Write(ciphertext);
 
Console.WriteLine($"Datoteka šifrirana: {outputFile}");
Console.WriteLine($"  Original: {plaintext.Length:N0} bajtova");
Console.WriteLine($"  Šifrirano: {output.Length:N0} bajtova");

Primjer koda: Dešifriranje datoteke

using var ctx = PqCryptoContext.Initialize();
 
string encryptedFile = "document.pdf.enc";
string outputFile = "document-decrypted.pdf";
string password = "MySecurePassword123!";
 
using var input = File.OpenRead(encryptedFile);
using var reader = new BinaryReader(input);
 
// Provjera magic numbera
var magic = Encoding.ASCII.GetString(reader.ReadBytes(5));
if (magic != "PQENC")
    throw new InvalidDataException("Nevaljani format datoteke");
 
// Provjera verzije
var version = reader.ReadByte();
if (version != 1)
    throw new NotSupportedException($"Verzija {version} nije podržana");
 
// Čitanje parametara
var saltLen = reader.ReadInt32();
var salt = reader.ReadBytes(saltLen);
var nonceLen = reader.ReadInt32();
var nonce = reader.ReadBytes(nonceLen);
var tagLen = reader.ReadInt32();
var tag = reader.ReadBytes(tagLen);
var ciphertextLen = reader.ReadInt32();
var ciphertext = reader.ReadBytes(ciphertextLen);
 
// Derivacija ključa (isti parametri!)
var key = ctx.DeriveKeyArgon2id(
    password: Encoding.UTF8.GetBytes(password),
    salt: salt,
    outputLength: 32,
    iterations: 3,
    memoryKiB: 65536,
    parallelism: 4
);
 
// Dešifriranje
var plaintext = new byte[ciphertext.Length];
 
using var aes = new OpenSslAesGcm(key);
aes.Decrypt(nonce, ciphertext, tag, plaintext);
 
// Spremanje
File.WriteAllBytes(outputFile, plaintext);
 
Console.WriteLine($"Datoteka dešifrirana: {outputFile}");

Streaming za velike datoteke

public class FileEncryptor
{
    private const int ChunkSize = 64 * 1024;  // 64 KB dijelovi
 
    public void EncryptLargeFile(
        string inputPath,
        string outputPath,
        byte[] key)
    {
        using var ctx = PqCryptoContext.Initialize();
        using var input = File.OpenRead(inputPath);
        using var output = File.Create(outputPath);
        using var writer = new BinaryWriter(output);
 
        // Zapisivanje zaglavlja
        writer.Write(Encoding.ASCII.GetBytes("PQENC"));
        writer.Write((byte)2);  // Verzija 2 = Streaming
 
        // Broj dijelova
        var totalChunks = (int)Math.Ceiling((double)input.Length / ChunkSize);
        writer.Write(totalChunks);
 
        var buffer = new byte[ChunkSize];
        var chunkIndex = 0;
 
        while (input.Position < input.Length)
        {
            var bytesRead = input.Read(buffer, 0, ChunkSize);
            var chunk = buffer.AsSpan(0, bytesRead).ToArray();
 
            // Jedinstveni nonce po dijelu (ChunkIndex + Random)
            var nonce = new byte[12];
            BitConverter.GetBytes(chunkIndex).CopyTo(nonce, 0);
            RandomNumberGenerator.Fill(nonce.AsSpan(4));
 
            var ciphertext = new byte[bytesRead];
            var tag = new byte[16];
 
            using var aes = new OpenSslAesGcm(key);
            // AAD = Chunk-Index za zaštitu redoslijeda
            var aad = BitConverter.GetBytes(chunkIndex);
            aes.Encrypt(nonce, chunk, ciphertext, tag, aad);
 
            // Zapisivanje dijela
            writer.Write(nonce);
            writer.Write(tag);
            writer.Write(ciphertext.Length);
            writer.Write(ciphertext);
 
            chunkIndex++;
        }
 
        Console.WriteLine($"Šifrirano: {chunkIndex} dijelova");
    }
 
    public void DecryptLargeFile(
        string inputPath,
        string outputPath,
        byte[] key)
    {
        using var input = File.OpenRead(inputPath);
        using var reader = new BinaryReader(input);
        using var output = File.Create(outputPath);
 
        // Čitanje zaglavlja
        var magic = Encoding.ASCII.GetString(reader.ReadBytes(5));
        if (magic != "PQENC") throw new InvalidDataException();
 
        var version = reader.ReadByte();
        if (version != 2) throw new NotSupportedException();
 
        var totalChunks = reader.ReadInt32();
 
        for (int i = 0; i < totalChunks; i++)
        {
            var nonce = reader.ReadBytes(12);
            var tag = reader.ReadBytes(16);
            var ciphertextLen = reader.ReadInt32();
            var ciphertext = reader.ReadBytes(ciphertextLen);
 
            var plaintext = new byte[ciphertextLen];
 
            using var aes = new OpenSslAesGcm(key);
            var aad = BitConverter.GetBytes(i);
            aes.Decrypt(nonce, ciphertext, tag, plaintext, aad);
 
            output.Write(plaintext);
        }
    }
}

Hibridno šifriranje s ML-KEM

public class HybridFileEncryptor
{
    public void EncryptForRecipient(
        string inputPath,
        string outputPath,
        byte[] recipientMlKemPublicKey)
    {
        using var ctx = PqCryptoContext.Initialize();
 
        // ML-KEM enkapsulacija ključa
        var pubKey = ctx.ImportPublicKey(recipientMlKemPublicKey);
        var (kemCiphertext, sharedSecret) = ctx.Encapsulate(pubKey);
 
        // Derivacija ključa za šifriranje datoteke
        var fileKey = ctx.DeriveKey(
            sharedSecret,
            outputLength: 32,
            info: Encoding.UTF8.GetBytes("file-encryption-key")
        );
 
        // Šifriranje datoteke
        var encryptor = new FileEncryptor();
        var tempFile = Path.GetTempFileName();
        encryptor.EncryptLargeFile(inputPath, tempFile, fileKey);
 
        // Output s KEM Ciphertextom
        using var output = File.Create(outputPath);
        using var writer = new BinaryWriter(output);
 
        writer.Write(Encoding.ASCII.GetBytes("PQKEM"));
        writer.Write((byte)1);
        writer.Write(kemCiphertext.Length);
        writer.Write(kemCiphertext);
 
        // Dodavanje šifriranog sadržaja
        using var encryptedContent = File.OpenRead(tempFile);
        encryptedContent.CopyTo(output);
 
        File.Delete(tempFile);
    }
}

Specifikacija formata datoteke

PQENC v1 (Single-Shot):
┌─────────────────────────────────┐
│ Magic: "PQENC" (5 bajtova)      │
│ Version: 0x01 (1 bajt)          │
│ Salt Length (4 bajta)           │
│ Salt (varijabilno)              │
│ Nonce Length (4 bajta)          │
│ Nonce (12 bajtova)              │
│ Tag Length (4 bajta)            │
│ Tag (16 bajtova)                │
│ Ciphertext Length (4 bajta)     │
│ Ciphertext (varijabilno)        │
└─────────────────────────────────┘

PQENC v2 (Streaming):
┌─────────────────────────────────┐
│ Magic: "PQENC" (5 bajtova)      │
│ Version: 0x02 (1 bajt)          │
│ Total Chunks (4 bajta)          │
├─────────────────────────────────┤
│ Chunk 0:                        │
│   Nonce (12 bajtova)            │
│   Tag (16 bajtova)              │
│   Ciphertext Length (4 bajta)   │
│   Ciphertext (varijabilno)      │
├─────────────────────────────────┤
│ Chunk 1: ...                    │
└─────────────────────────────────┘

Povezani scenariji

Odnos Scenarij Opis
Komponenta 7.2 Enkapsulacija ključa ML-KEM za primatelja
Povezano 7.1 Hibridno šifriranje Hibridni koncept
Povezano 4.4 Sigurnosna kopija Šifriranje kopija

« ← 7.2 Enkapsulacija ključa | ↑ Pregled šifriranja | → Svi scenariji »


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: 30.01.2026. u 07:24