# Veřejný a soukromý klíč

Asymetrická kryptografie je nejméně intuitivní věc v celé síťařině. Symetrická šifra dává smysl každému — máme společné heslo, tak si šifrujeme. Ale představa, že si můžeme vyměnit klíče **veřejně, před zraky útočníka**, a přesto se domluvit na tajemství, vypadá na první pohled jako podvod.

Není. Tahle stránka vysvětluje, jak to funguje, kde je to v praxi vidět a co s klíči doopravdy dělat.

Základní pojmy jsou na [stavebních kamenech](Sifrovani-zaklady), samotná výměna klíčů na [Diffie-Hellman](Diffie-Hellman).

## Základní myšlenka

Máš **dvojici klíčů**, které vzniknou najednou a patří k sobě:

**Soukromý klíč** — jediná kopie, nikdy neopustí tvůj stroj. Z něj se dá spočítat veřejný klíč.

**Veřejný klíč** — rozdáš ho komukoliv. Klidně ho pověsíš na web. Ze znalosti veřejného klíče **nejde odvodit soukromý** — tohle je celá pointa a stojí na tom, že určité matematické operace se snadno počítají jedním směrem a prakticky nedají zvrátit.

Z toho plynou dvě různé operace, které se často pletou:

| Operace | Kdo použije **soukromý** | Kdo použije **veřejný** | K čemu |
|---|---|---|---|
| **Šifrování** | příjemce dešifruje | odesílatel šifruje | důvěrnost |
| **Podpis** | autor podepisuje | kdokoliv ověří | autenticita |

Všimni si, že jsou to **opačné směry**. Když chceš někomu poslat tajnou zprávu, potřebuješ **jeho veřejný klíč**. Když chceš dokázat, že zpráva je od tebe, použiješ **svůj soukromý klíč**.

```mermaid
flowchart LR
    subgraph sif [Šifrování]
    A[Zpráva] -->|veřejný klíč Boba| B[Šifrový text]
    B -->|soukromý klíč Boba| C[Zpráva]
    end
    subgraph pod [Podpis]
    D[Zpráva] -->|soukromý klíč Alice| E[Podpis]
    E -->|veřejný klíč Alice| F[platí / neplatí]
    end
```

### „Podpis je šifrování soukromým klíčem"

Tuhle větu najdeš ve stovkách návodů a **je nesprávná**. Vypadá pravdivě jen u RSA, protože jeho matematika je symetrická. U eliptických křivek — tedy u toho, co dnes používáš — je podpis úplně jiný algoritmus než šifrování a takové srovnání nedává smysl vůbec.

Správně: podpis a šifrování jsou dvě samostatné schopnosti. Některé rodiny umí obojí (RSA), některé jen jednu (ECDSA a Ed25519 umí jen podepisovat, X25519 umí jen výměnu klíčů).

## Proč to funguje

Není potřeba počítat, ale bez téhle intuice zůstane všechno magie. Existují matematické operace, které jsou **snadné jedním směrem a extrémně drahé zpátky**:

**Násobení versus rozklad na součin.** Vynásobit dvě tisícimístná prvočísla umí telefon okamžitě. Dostat je z výsledku zpátky nikdo neumí. Na tom stojí **RSA**.

**Umocňování ve zbytkové aritmetice versus diskrétní logaritmus.** Spočítat `g^a mod p` je snadné. Dostat z výsledku `a` je nezvládnutelné. Na tom stojí **klasický Diffie-Hellman** a **DSA**.

**Násobení bodu na eliptické křivce versus totéž zpátky.** Sečíst bod sám se sebou `a`-krát je rychlé. Zjistit z výsledku `a` je řádově těžší než u předchozích. Na tom stojí **ECDH, ECDSA a Ed25519**.

To „extrémně drahé" má konkrétní čísla. Rozklad 2048bitového čísla je mimo dnešní možnosti kohokoliv na planetě. Rozklad 512bitového šel v roce 1999 a dnes to zvládne cloudová instance za pár hodin — proto se délky klíčů posouvají.

Zásadní upozornění: **žádná z těch tří věcí není dokázaná jako těžká.** Jen to nikdo padesát let nedokázal zlomit. To je slabší tvrzení, než jak se obvykle prezentuje, a je to důvod, proč se kryptografie umí měnit.

## Proč jsou křivky lepší

Rozdíl je v tom, jak dobře fungují nejlepší známé útoky.

Na RSA a klasický DH existuje **síto v číselném tělese** — algoritmus, který sice trvá dlouho, ale roste podexponenciálně. Zdvojnásobení délky klíče proto sílu nezdvojnásobí, jen o kus přidá.

Na eliptických křivkách nic lepšího než obecné metody (Pollardovo rho) nikdo nenašel. Ty trvají zhruba odmocninu z velikosti skupiny, což je **plně exponenciální** v délce klíče. Každý přidaný bit klíče tedy stojí útočníka dvojnásobek.

Praktický důsledek:

| | RSA-3072 | P-256 / X25519 |
|---|---|---|
| Bezpečnostní úroveň | 128 bitů | 128 bitů |
| Veřejný klíč | ~400 B | **32 B** |
| Podpis | 384 B | 64 B |
| Generování klíče | sekundy | milisekundy |
| Podepsání | rychlé | rychlé |
| Ověření podpisu | **velmi rychlé** | rychlé |

RSA má jedinou přetrvávající výhodu: ověření podpisu je díky malému veřejnému exponentu (65537) mimořádně levné. Proto se pořád drží tam, kde se jeden podpis ověřuje milionkrát. Jinak nemá důvod.

## Rodiny, které potkáš

### RSA

Nejstarší (1977) a nejrozšířenější. Umí jako jediný z běžných algoritmů **podepisovat i šifrovat**.

```bash
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out rsa.pem
openssl pkey -in rsa.pem -pubout -out rsa.pub
openssl pkey -in rsa.pem -text -noout | head
```

Pravidla, pokud RSA používat musíš:

- **Minimálně 2048 bitů, raději 3072.** Pod 2048 nic. Nad 4096 jen plýtváš výkonem.
- **Zarovnání OAEP pro šifrování**, nikdy PKCS#1 v1.5. Ten má útok, který se pod různými jmény (Bleichenbacher, ROBOT) vrací každých pár let.
- **Zarovnání PSS pro podpisy**, kde to protistrana umí.

**RSA neumí zašifrovat velká data.** Ta zpráva se musí vejít do klíče minus zarovnání — u 2048bitového klíče s OAEP a SHA-256 je to **190 bajtů**. Proto se v reálu RSA nikdy nešifruje obsah, jen se jím přenese symetrický klíč. Tomu se říká *key transport* a TLS 1.3 ho úplně zrušilo, protože nemá [dopředné utajení](Diffie-Hellman#dopředné-utajení).

### Eliptické křivky NIST (P-256, P-384)

`prime256v1` / `secp256r1` a `secp384r1`. Standardní, všude podporované, používané ve většině certifikátů.

Jsou v pořádku, ale **implementují se hůř** — jsou u nich potřeba kontroly platnosti bodu, převody souřadnic a snadno vzniknou postranní kanály. Navíc jsou u nich neveřejně zvolené konstanty, což od roku 2013 živí trvalou nedůvěru.

### Curve25519: X25519 a Ed25519

Návrh z roku 2005, dnes výchozí volba. Jedna křivka, dvě použití, **která se nedají zaměňovat**:

**X25519** — výměna klíčů (ECDH). Používá ji TLS 1.3, SSH i [WireGuard](WireGuard). Veřejný klíč 32 bajtů, sdílené tajemství 32 bajtů.

**Ed25519** — podpisy (EdDSA). Používá ji SSH, podepisování Git commitů, `age`, moderní balíčkovací systémy. Veřejný klíč 32 bajtů, podpis 64 bajtů.

Proč je to lepší než P-256:

- **Všechny konstanty jsou odvozené a vysvětlené.** Nic není „prostě zvolené".
- **Nemá okrajové případy.** Jakýchkoliv 32 bajtů je platný veřejný klíč, takže nejde poslat záludný vstup.
- **Implementace je přirozeně v konstantním čase**, takže postranní kanály nevznikají mimoděk.
- **Ed25519 je deterministické.** Náhodné číslo pro podpis se odvodí z klíče a zprávy, ne z generátoru. Tím odpadá nejhorší selhání ECDSA — viz níže.

### ECDSA a smrtelná past s nonce

ECDSA potřebuje ke každému podpisu jednorázové náhodné číslo `k`. Když se `k` **zopakuje u dvou různých zpráv**, dá se ze dvou podpisů přímo dopočítat soukromý klíč. Ne oslabit, přímo spočítat.

Není to teorie:

- **Sony PlayStation 3** (2010) používalo pro všechny podpisy pevné `k`. Podpisový klíč konzole byl veřejně odvozen a nešel opravit, protože byl v hardwaru.
- **Android** (2013) měl vadný generátor náhodných čísel a z peněženek Bitcoinu tak zmizely reálné peníze.

Ed25519 tenhle problém nemá z podstaty návrhu. Když si vybíráš a máš na výběr, ber Ed25519.

### Co je mrtvé

**DSA** — 1024bitové omezení, stejná past s `k`, dnes nemá jediný důvod existovat. OpenSSH ho v roce 2024 vypnul a v roce 2025 z kódu odstranil.

**RSA se SHA-1** (`ssh-rsa`) — OpenSSH ho vypnul ve verzi 8.8. Když ti staré zařízení hlásí `no matching host key type`, je to tohle. Řešením je zařízení aktualizovat, ne obcházet.

**secp256k1** — křivka Bitcoinu. Je v pořádku, ale mimo kryptoměny nemá důvod.

## Formáty klíčů, aneb odkud pochází zmatek

Jeden a tentýž klíč potkáš ve čtyřech zápisech a je užitečné vědět, který je který.

**PEM** — textový, `-----BEGIN ... -----`, obsah je base64. Nejběžnější u TLS.

```
-----BEGIN PRIVATE KEY-----      ← PKCS#8, moderní, jakýkoliv algoritmus
-----BEGIN RSA PRIVATE KEY-----  ← PKCS#1, jen RSA, staré
-----BEGIN EC PRIVATE KEY-----   ← SEC1, jen křivky, staré
-----BEGIN OPENSSH PRIVATE KEY-----  ← formát OpenSSH, ne OpenSSL
-----BEGIN CERTIFICATE-----      ← to není klíč, to je certifikát
```

**DER** — totéž binárně, bez base64. Používá Java a Windows, přípona `.der` nebo `.cer`.

**PKCS#12 / PFX** — jeden soubor `.p12` obsahující klíč **i** certifikát **i** řetěz, chráněný heslem. Windows, Java, importy do prohlížeče.

**Formát OpenSSH** — `~/.ssh/id_ed25519`. Vlastní formát, který se od OpenSSL liší a od verze 7.8 je výchozí. Umí soukromý klíč chránit heslem přes `bcrypt_pbkdf`, což ho dělá odolnějším proti hádání hesla než starý formát.

Převody, které budeš dřív nebo později potřebovat:

```bash
# PEM → DER
openssl pkey -in key.pem -outform DER -out key.der

# starý PKCS#1 → moderní PKCS#8
openssl pkey -in old-rsa.pem -out new.pem

# klíč + certifikát → PKCS#12
openssl pkcs12 -export -inkey key.pem -in cert.pem -certfile chain.pem -out bundle.p12

# PKCS#12 → PEM
openssl pkcs12 -in bundle.p12 -nodes -out all.pem

# SSH veřejný klíč z privátního (když ho ztratíš)
ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
```

**Chyba, která se stane každému:** zaměnit certifikát a klíč. Certifikát je veřejný a začíná `BEGIN CERTIFICATE`. Klíč je tajný a začíná `BEGIN ... PRIVATE KEY`. Nginx si na to bude stěžovat větou `PEM_read_bio_X509` a nebude vysvětlovat proč.

## Jak zjistit, co vlastně mám

```bash
# typ a parametry klíče
openssl pkey -in key.pem -text -noout

# patří tenhle klíč k tomuhle certifikátu?
openssl pkey -in key.pem -pubout | openssl sha256
openssl x509 -in cert.pem -pubkey -noout | openssl sha256
# obě čísla musí být stejná

# co je v certifikátu
openssl x509 -in cert.pem -noout -subject -issuer -dates -ext subjectAltName

# SSH klíč: typ, délka a otisk
ssh-keygen -lf ~/.ssh/id_ed25519.pub
```

To porovnání otisků je nejrychlejší způsob, jak vyloučit nejčastější příčinu toho, že server po výměně certifikátu nenaběhne.

## Fingerprint

Veřejné klíče se špatně porovnávají očima. Proto se z nich dělá **otisk** — hash veřejného klíče, který se dá přečíst do telefonu.

```bash
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# 256 SHA256:5N3Yb... martin@notebook (ED25519)
```

Otisk **nenahrazuje ověření**, jen ho zkracuje. Když otisk získáš tou samou cestou, kterou by šel útočník, nedokázal jsi nic. Tohle je celý problém prvního připojení na SSH server, podrobně na [SSH](SSH#first-use-a-known_hosts).

Historicky se používal MD5 (`ssh-keygen -E md5`) a u nich ta dvojtečkovaná šestnáctková čísla. Dnes SHA-256 v base64.

## Kde se s tím v síti setkáš

| Situace | Kdo má soukromý klíč | Co dokazuje |
|---|---|---|
| **HTTPS** | server | že web patří tomu, komu [CA](Certifikaty-a-Lets-Encrypt) vystavila certifikát |
| **SSH — host key** | server | že jsi u toho stroje jako minule |
| **SSH — klíč uživatele** | ty | že jsi ten, kdo má klíč v `authorized_keys` |
| **mTLS** | obě strany | vzájemně; vyžaduje [vlastní CA](Vlastni-certifikacni-autorita) |
| **[WireGuard](WireGuard)** | oba peery | že protistrana zná klíč z konfigurace |
| **Podpis balíčků APT** | správce repozitáře | že balíček nikdo nepodstrčil |
| **Podpis Git commitu** | autor | kdo commit vytvořil |
| **Passkey** | tvoje zařízení | přihlášení bez hesla |

Podstatné je, že ve všech případech jde o **totéž**. Naučíš se to jednou.

## Certifikát: podepsané tvrzení o klíči

Veřejný klíč sám o sobě neříká, komu patří. Certifikát je **veřejný klíč plus jméno plus doba platnosti, to celé podepsané někým, komu se dá věřit**.

```
certifikát = { veřejný klíč, jména (SAN), platnost od–do, účel }
           + podpis certifikační autority přes to všechno
```

Když prohlížeč certifikát dostane, ověří podpis veřejným klíčem té CA — a její certifikát ověří proti další, až ke [kořenu, který má v systému](TLS-a-HTTPS#řetěz-certifikátů).

Certifikát tedy **nezvyšuje sílu šifrování ani o bit**. Řeší výhradně otázku, komu klíč patří.

## Jak s klíči zacházet

**Soukromý klíč se negeneruje jinde, než kde bude použitý.** Když ti někdo pošle klíč mailem, není to tvůj klíč, je to sdílený klíč.

**Nikdy ho neposílej.** Ani sobě, ani do chatu, ani do repozitáře. Když se to stane, klíč je spálený a musí se vyměnit — smazání commitu nestačí, historie zůstává.

**Chraň ho heslem.** U SSH:

```bash
ssh-keygen -t ed25519 -a 100 -C "martin@notebook"
```

`-a 100` je počet kol funkce odvození klíče z hesla. Zpomalí to hádání hesla, kdyby se soubor ztratil, a při přihlašování to nepoznáš. Aby tě heslo neotravovalo, je [ssh-agent](SSH#ssh-agent).

**Práva 600.** OpenSSH klíč s právy pro skupinu odmítne použít a udělá dobře.

```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
```

**Různé klíče na různé účely.** Jeden pro server doma, jiný pro práci, jiný pro GitHub. Ztráta jednoho pak neznamená ztrátu všeho a odebrat jeden přístup je jeden řádek.

**Nejlepší je klíč, který nejde zkopírovat.** Hardwarový token (YubiKey, Nitrokey) drží soukromý klíč uvnitř čipu a operaci provádí sám. Malware ti ho nemůže ukrást, protože se nikdy neobjeví v paměti. OpenSSH to podporuje od verze 8.2:

```bash
ssh-keygen -t ed25519-sk -O resident -O verify-required
```

Vznikne klíč, který vyžaduje **dotek tokenu** u každého přihlášení a `verify-required` k tomu přidá PIN. `resident` znamená, že klíč lze z tokenu obnovit na novém počítači (`ssh-keygen -K`). Za devět stovek je tohle největší skok v bezpečnosti, jaký si můžeš koupit.

**Měj plán na výměnu.** Předem věz, jak by ses dostal na server, kdyby ti notebook shořel. Druhý klíč v trezoru, konzole u poskytovatele, přístup přes [VPN](WireGuard) z jiného stroje.

## Praktické: podepsat a ověřit soubor

Nemusíš na to GPG. SSH klíč, který už máš, to umí od OpenSSH 8.0:

```bash
# podepsat
ssh-keygen -Y sign -f ~/.ssh/id_ed25519 -n file dokument.pdf
# vznikne dokument.pdf.sig

# ověřit — potřebuješ seznam povolených podepisujících
echo "martin@example.cz $(cat ~/.ssh/id_ed25519.pub)" > allowed_signers
ssh-keygen -Y verify -f allowed_signers -I martin@example.cz \
    -n file -s dokument.pdf.sig < dokument.pdf
```

Stejným mechanismem se podepisují Git commity, bez GPG a bez keyserverů:

```bash
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
```

## Kam dál

- **[Diffie-Hellman](Diffie-Hellman)** — jak se dvě strany domluví na společném tajemství
- **[Hashe, HMAC a podpisy](Hashe-a-podpisy)** — co se vlastně podepisuje
- **[SSH do hloubky](SSH)** — nejčastější místo, kde tyhle klíče použiješ
- **[Vlastní certifikační autorita](Vlastni-certifikacni-autorita)** — když si chceš certifikáty vystavovat sám
- **[Náhodnost a entropie](Nahodnost-a-entropie)** — proč se klíče lámou
