---
title: "Autentifikované šifrovanie — Crypto for Laravel | Roundly"
description: "AES-256-GCM (RFC 5116) s pridruženými dátami, ktoré viažu každý šifrový text k jeho záznamu či účelu — seal a open, kľúče, nonce a chyby."
url: https://roundly-consulting.com/sk/open-source/navody/crypto-for-laravel/autentifikovane-sifrovanie
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Crypto for Laravel](https://roundly-consulting.com/sk/open-source/navody/crypto-for-laravel.md)

# Autentifikované šifrovanie

Aead\\Aes256Gcm implementuje AEAD\_AES\_256\_GCM (RFC 5116 §5.2, NIST SP 800-38D) nad ext-openssl: 32-bajtový kľúč, 12-bajtový nonce a 16-bajtový autentifikačný tag pripojený za šifrový text. Šifrovanie je tu autentifikované — open() a decrypt() vrátia otvorený text len vtedy, keď tag dokáže, že šifrový text, nonce aj pridružené dáta sú presne tie, ktoré sa pod daným kľúčom zapečatili. Čokoľvek iné vyhodí výnimku a nič sa nedešifruje:

```php
use RoundlyConsulting\Crypto\Aead\Aes256Gcm;
use RoundlyConsulting\Crypto\Aead\DecryptionFailedException;
use RoundlyConsulting\Crypto\Facades\Crypto;

// A key is 32 random bytes. Keep it like any other secret, never next to the data.
$key = Crypto::randomBytes(Aes256Gcm::KEY_BYTES);

$aead = Crypto::aes256Gcm(); // or new Aes256Gcm

// A fresh random nonce, carried in front: nonce ‖ ciphertext ‖ tag.
$sealed = $aead->seal($key, $plaintext, associatedData: 'invoices:42');

try {
    $plaintext = $aead->open($key, $sealed, associatedData: 'invoices:42');
} catch (DecryptionFailedException) {
    // wrong key or context, or a changed ciphertext — one message for every cause
}
```

Pridružené dáta (associated data) sa autentifikujú, no nešifrujú — vložte do nich všetko, k čomu má šifrový text zostať viazaný (účel, identitu záznamu), a šifrový text skopírovaný kamkoľvek inam sa neotvorí. Trieda je final readonly, nemá argumenty konštruktora a nedrží žiadny kľúčový materiál: každý kľúč sa odovzdáva pri volaní a je označený #\[\\SensitiveParameter\]. Fasáda, injektovaný manažér aj samotná trieda spúšťajú rovnaký kód; Crypto::aes256Gcm() zakaždým vráti novú inštanciu — je bezstavová, takže jej ukladanie nič neprinesie:

```php
use RoundlyConsulting\Crypto\Aead\Aes256Gcm;
use RoundlyConsulting\Crypto\CryptoManager;
use RoundlyConsulting\Crypto\Facades\Crypto;

Crypto::aes256Gcm()->seal($key, $plaintext, $aad);                    // facade
app(CryptoManager::class)->aes256Gcm()->seal($key, $plaintext, $aad); // injected manager
(new Aes256Gcm)->seal($key, $plaintext, $aad);                        // the class itself
```

## Kedy ho použiť

| Potrebujete | Použite |
| --- | --- |
| Zašifrovať hodnotu kľúčom aplikácie bez ničoho, k čomu by ju bolo treba viazať | Crypt / encrypt() z Laravelu — používa APP\_KEY, rotuje cez APP\_PREVIOUS\_KEYS a nemá vstup pre pridružené dáta. |
| Šifrový text, ktorý sa smie otvoriť len vo svojom kontexte — tento záznam, tento tenant, tento účel | Aes256Gcm s pridruženými dátami. |
| Iný kľúč než APP\_KEY — jeden kľúč na účel alebo kľúč, ktorý drží iná služba | Aes256Gcm. |
| Kompaktný binárny formát, ktorý prečíta každá implementácia RFC 5116 / AES-256-GCM | Aes256Gcm::encrypt() alebo seal(). |
| Dokázať pravosť správy, ktorá zostáva čitateľná — webhooky, podpísané URL | Hash\\Hmac, nie šifrovanie — pozrite HMAC, hašovanie a HKDF. |
| Hodnotu na vyhľadávanie, ktorú nikdy netreba dešifrovať | Hash\\Digest. |

## Veľkosti a metódy

| Konštanta | Hodnota | Význam |
| --- | --- | --- |
| `Aes256Gcm::KEY_BYTES` | 32 | Dĺžka kľúča (AES-256). |
| `Aes256Gcm::NONCE_BYTES` | 12 | Dĺžka nonce (96 bitov, predvolená pre GCM). |
| `Aes256Gcm::TAG_BYTES` | 16 | Dĺžka tagu (128 bitov, pripojený za šifrový text). |

Iné veľkosti trieda nepodporuje — žiadne AES-128 ani AES-192, žiadne kratšie tagy ani iné dĺžky nonce. Kľúč alebo nonce inej dĺžky vyhodí InvalidAeadParameterException.

| Metóda | Vracia |
| --- | --- |
| `seal(string $key, string $plaintext, string $associatedData = '')` | nonce ‖ šifrový text ‖ tag pod novým náhodným nonce. |
| `open(string $key, string $sealed, string $associatedData = '')` | Otvorený text výstupu zo seal(). |
| `encrypt(string $key, string $nonce, string $plaintext, string $associatedData = '')` | šifrový text ‖ tag — nonce nie je súčasťou. |
| `decrypt(string $key, string $nonce, string $ciphertext, string $associatedData = '')` | Otvorený text výstupu z encrypt(). |

- seal() a open() sú dvojica na bežné použitie: nonce sa vygeneruje a prenesie za vás.
- encrypt() a decrypt() sú rozhranie podľa RFC 5116: nonce dodávate a ukladáte sami.
- seal() vráti NONCE\_BYTES + strlen($plaintext) + TAG\_BYTES bajtov (réžia 28 bajtov); encrypt() vráti strlen($plaintext) + TAG\_BYTES. Prázdny otvorený text je povolený a zapečatí sa do 28 bajtov.
- GCM nepoužíva zarovnanie (padding), takže šifrový text je rovnako dlhý ako otvorený text — šifrovanie skryje obsah, nie dĺžku.
- Dve zapečatenia toho istého textu pod tým istým kľúčom sa líšia, pretože každé použije nový nonce.
- Celá správa sa spracúva v pamäti; streamovacie API neexistuje.

## Na vstupe aj na výstupe surové bajty

Kľúče, nonce aj šifrové texty sú binárne reťazce. Pred uložením do textového stĺpca (alebo do JSON či URL) ich zakódujte a pred otvorením dekódujte. Binárny stĺpec ($table->binary()) ich prijme tak, ako sú:

```php
$stored = Crypto::base64Encode($aead->seal($key, $plaintext, associatedData: 'invoices:42'));

$plaintext = $aead->open($key, Crypto::base64Decode($stored), associatedData: 'invoices:42');
```

## Pridružené dáta viažu šifrový text k jeho kontextu

Pridružené dáta sa neukladajú — výstup ich nijako nenesie. Do seal() aj open() odovzdajte rovnaké bajty: pri otváraní ich znova zostavte z kontextu (vyššie invoices:42 z práve čítanej faktúry) a držte ich formát stabilný — iný reťazec sa neotvorí, ani keď znamená to isté. Použite ich na všetko, od čoho sa šifrový text nesmie nikdy oddeliť: účel, záznam, ku ktorému patrí (tabuľka, primárny kľúč, stĺpec), tenant či vlastník, verzia formátu alebo schémy:

```php
$aadFor = fn (int $id): string => implode('|', ['invoices', $id, 'iban']);

$sealed = $aead->seal($key, $iban, $aadFor(42));

$aead->open($key, $sealed, $aadFor(42));   // the IBAN

// The same bytes moved to invoice 43's row don't decrypt there. This throws
// DecryptionFailedException:
$aead->open($key, $sealed, $aadFor(43));
```

Bez pridružených dát by útočník s právom zápisu do databázy mohol vymeniť šifrové texty dvoch riadkov a aplikácia by každý z nich dešifrovala na nesprávnom mieste. S nimi sa vymenený text neotvorí.

- Predvolená hodnota je prázdny reťazec '' (bez kontextu); open() potom musí tiež dostať ''.
- Pridružené dáta nie sú tajné — nevkladajte do nich nič, čo by ste neuložili ako otvorený text.
- Zostavte ich zo stabilných hodnôt, ktoré viete pri dešifrovaní znova vypočítať — z primárneho kľúča, nie z meniacej sa časovej pečiatky.
- Časti spájajte oddeľovačom, ktorý sa vo vnútri časti nemôže vyskytnúť, aby \['a|b', 'c'\] a \['a', 'b|c'\] nikdy nedali rovnaký reťazec.

## Kľúče

Kľúč je presne 32 náhodných bajtov — nikdy nie heslo ani iný reťazec zvolený človekom. Balík nepotrebuje konfiguráciu, preto sám nikdy kľúč nenačíta: kľúč načíta váš kód a odovzdá ho:

```php
use RoundlyConsulting\Crypto\Aead\Aes256Gcm;
use RoundlyConsulting\Crypto\Facades\Crypto;

// Generate once, e.g. in a tinker session or a setup command
$key = Crypto::randomBytes(Aes256Gcm::KEY_BYTES);
echo Crypto::base64UrlEncode($key);       // put this in your secret store / .env

// Load at runtime, from your own config key
$key = Crypto::base64UrlDecode(config('services.vault.key'));
```

- Jeden kľúč na účel. Šifrovací kľúč nezdieľajte s podpisovaním, HMAC ani inou funkciou.
- Rotácia. Trieda prijíma jeden kľúč na volanie a nemá zväzok kľúčov. Pri rotácii si ponechajte starý kľúč, aby sa existujúce hodnoty dali otvoriť cez open(), nové hodnoty pečaťte cez seal() novým kľúčom a zaznamenajte, ktorý kľúč ktorú hodnotu zapečatil — napríklad v stĺpci s id kľúča vedľa šifrového textu.

Ak potrebujete viac kľúčov z jedného hlavného tajomstva, odvoďte ich cez Crypto::hkdf() (pozrite HMAC, hašovanie a HKDF) — bajt po bajte zhodne s natívnou funkciou PHP hash\_hkdf(), no dĺžka je vždy explicitná:

```php
$invoiceKey = Crypto::hkdf()->derive($masterKey, Aes256Gcm::KEY_BYTES, 'invoices');
$tokenKey   = Crypto::hkdf()->derive($masterKey, Aes256Gcm::KEY_BYTES, 'api-tokens');

// The same bytes as PHP's native hash_hkdf('sha256', $masterKey, Aes256Gcm::KEY_BYTES, 'invoices')
```

## Nonce

- seal() sa o nonce postará: vytiahne nový 96-bitový nonce z CSPRNG (Random\\Bytes), vloží ho na začiatok výstupu a open() ho odtiaľ prečíta. Vy ho nikdy neuvidíte.
- Nonce nikdy nepoužite pod tým istým kľúčom dvakrát. Pri GCM dve správy pod jedným kľúčom a jedným nonce prezradia XOR oboch otvorených textov a umožnia útočníkovi falšovať tagy — práve preto existuje seal().
- Pri náhodných nonce udržte počet zapečatení jedným kľúčom pod 2³² (NIST SP 800-38D §8.3); skôr, než k tomu dôjde, kľúč vymeňte alebo odvoďte kľúče pre jednotlivé účely.

encrypt() a decrypt() prijímajú váš nonce. Použite ich len vtedy, keď nonce určuje protokol alebo iný systém, prípadne ho ukladá oddelene — jedinečnosť je potom na vás:

```php
use RoundlyConsulting\Crypto\Aead\Aes256Gcm;
use RoundlyConsulting\Crypto\Facades\Crypto;

$aead       = Crypto::aes256Gcm();
$nonce      = Crypto::randomBytes(Aes256Gcm::NONCE_BYTES);     // or a counter you never repeat
$ciphertext = $aead->encrypt($key, $nonce, $plaintext, $associatedData); // ciphertext ‖ tag

$plaintext  = $aead->decrypt($key, $nonce, $ciphertext, $associatedData);
```

seal() je presne $nonce . encrypt($key, $nonce, …), takže open($key, $nonce.$ciphertext, $aad) otvorí aj výstup z encrypt().

## Interoperabilita

encrypt() vytvára štandardné AES-256-GCM — šifrový text, za ktorým nasleduje 128-bitový tag, s autentifikovanými pridruženými dátami. Prečíta ho každá vyhovujúca implementácia, ktorá má kľúč, nonce a pridružené dáta: openssl\_decrypt() priamo v PHP, crypto v Node, crypto/cipher v Go, cryptography v Pythone aj Web Crypto API (ktoré tiež očakáva šifrový text ‖ tag). Výstup zo seal() je to isté s 12-bajtovým nonce na začiatku:

```php
// The tag is the last 16 bytes; native OpenSSL opens it.
$plaintext = openssl_decrypt(
    substr($ciphertext, 0, -16), 'aes-256-gcm', $key, OPENSSL_RAW_DATA,
    $nonce, substr($ciphertext, -16), $associatedData,
);
```

## Chyby

| Výnimka | Vyhadzuje sa, keď | Význam |
| --- | --- | --- |
| `Aead\DecryptionFailedException` | open() alebo decrypt() nedokáže vstup autentifikovať: nesprávny kľúč, nonce či pridružené dáta, zmenený šifrový text alebo tag, prípadne vstup príliš krátky na tag (decrypt(): pod 16 bajtov; open(): pod 28 bajtov). | Problém s dátami — hodnotu považujte za cudziu alebo pozmenenú. |
| `Aead\InvalidAeadParameterException` | Kľúč nemá 32 bajtov (keyLength()), nonce nemá 12 bajtov (nonceLength()) alebo OpenSSL šifru odmietne (cipherUnavailable(), na funkčnom OpenSSL nedosiahnuteľné). | Programátorská chyba, nikdy nie vlastnosť dát. |

- DecryptionFailedException nesie vždy rovnakú správu — The ciphertext could not be authenticated; nothing was decrypted. — bez ohľadu na príčinu. Nikdy nepovie, ktorá časť zlyhala, a nikdy neobsahuje kľúč ani otvorený text.
- InvalidAeadParameterException uvádza len dĺžky, napríklad An AES-256-GCM key must be 32 bytes, \[16\] given.
- open() kontroluje dĺžku zapečateného vstupu skôr než kľúč: čokoľvek kratšie ako 28 bajtov (NONCE\_BYTES + TAG\_BYTES) vyhodí DecryptionFailedException, aj keď má nesprávnu dĺžku aj kľúč.
- Obe dedia z CryptoException — pozrite Výnimky.

Chybu dát zachyťte, programátorskú chybu nechajte prebublať:

```php
use RoundlyConsulting\Crypto\Aead\DecryptionFailedException;
use RoundlyConsulting\Crypto\Aead\InvalidAeadParameterException;

try {
    $secret = $aead->open($key, $sealed, $aad);
} catch (DecryptionFailedException) {
    abort(404);                         // not yours / tampered: don't explain why
}
// InvalidAeadParameterException is left to bubble up: it is a bug in the caller.
```

## Ako je to overené

Implementáciu pripínajú známe testovacie vektory AES-256 zo špecifikácie GCM (McGrew & Viega, príloha B, testovacie prípady 13–16, overené v oboch smeroch) a každý prípad AES-256 / 96-bitový nonce / 128-bitový tag z korpusu AES-GCM projektu Wycheproof — 39 platných prípadov (presný šifrový text aj tag v oboch smeroch) a 27 neplatných, z ktorých každý musí vyhodiť DecryptionFailedException.

## Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

[Ďalšie spôsoby podpory vrátane kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/crypto-for-laravel.md)
