SSH guide¶
Log in to another computer, run Git over SSH, copy files, all through an encrypted channel. Four parts: stop at the one you need.
In one minute · Do it · Expert · Now and next
In one minute¶
SSH lets you type on a computer far away as if you sat in front of it. Everything you send is locked so nobody on the way can read or change it.
It uses two pairs of keys:
- The server's key proves you reached the real server, not an impostor. Your computer remembers it in a file called
known_hosts. - Your key proves who you are, instead of a password. It has two halves: the private key stays on your computer and is never shared; the public key goes on every server you log in to.
Think of a padlock you hand out (public) and the only key that opens it (private). You can give padlocks to anyone; you never give the key.
Do it¶
The commands work on macOS, Linux and Windows 10 or later (OpenSSH is built in).
Make a key pair (Ed25519, the default and best choice). Give it a passphrase when asked.
ssh-keygen -t ed25519 -C "you@example.com"
This writes ~/.ssh/id_ed25519 (private, keep it) and ~/.ssh/id_ed25519.pub (public, share it).
Put the public key on a server, or on GitHub
ssh-copy-id -i ~/.ssh/id_ed25519.pub alice@server.example.com # a server
cat ~/.ssh/id_ed25519.pub # GitHub: paste this under Settings > SSH keys
ssh -T git@github.com # test: "Hi alice! You've successfully authenticated"
Type the passphrase once per session with the agent
eval "$(ssh-agent -s)"; ssh-add ~/.ssh/id_ed25519 # Linux, Windows (Git Bash)
ssh-add --apple-use-keychain ~/.ssh/id_ed25519 # macOS: remembered in the Keychain
Check fingerprints (compare with what the server's admin or GitHub publishes)
ssh-keygen -lf ~/.ssh/id_ed25519.pub # 256 SHA256:S+bx... you@example.com (ED25519)
ssh-keygen -R server.example.com # forget a server's old key after a real, announced change
Convert key formats
| From → to | Command |
|---|---|
OpenSSH private key → PEM (BEGIN RSA PRIVATE KEY, old tools) |
ssh-keygen -p -m PEM -f id_rsa (rewrites the file in place: copy it first) |
OpenSSH private key → PKCS#8 (what openssl pkey reads) |
ssh-keygen -p -m PKCS8 -f id_ecdsa |
OpenSSH public key → BEGIN PUBLIC KEY (OpenSSL) |
ssh-keygen -e -m PKCS8 -f id_rsa.pub |
| PEM / PKCS#8 public key → OpenSSH line | ssh-keygen -i -m PKCS8 -f key.pem |
OpenSSH → PuTTY .ppk |
puttygen id_ed25519 -O private -o key.ppk |
PuTTY .ppk → OpenSSH |
puttygen key.ppk -O private-openssh-new -o id_ed25519 |
| Private key → its public key again | ssh-keygen -y -f id_ed25519 > id_ed25519.pub |
More commands (certificates, signing Git commits and files, revocation): ssh-keygen. What is inside each file format: key and certificate files.
Expert¶
The algorithms, one per job¶
Every connection picks one algorithm for each job: the first on the client's list that the server also has. Full lists, with what Apple ships: SSH protocol.
| Job | Use | Standard | Avoid |
|---|---|---|---|
| Key exchange | mlkem768x25519-sha256 (post-quantum hybrid), sntrup761x25519-sha512, curve25519-sha256 |
RFC 10042, RFC 9941, RFC 8731 | diffie-hellman-group1-sha1, -group14-sha1, -group-exchange-sha1 (RFC 9142) |
| Host and user keys (signatures) | ssh-ed25519; rsa-sha2-512 / rsa-sha2-256 for old systems |
RFC 8709, RFC 8332 | ssh-rsa (RSA with SHA-1), ssh-dss (DSA) |
| Cipher | chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com |
OpenSSH PROTOCOL.chacha20poly1305, RFC 5647 |
*-cbc, 3des-cbc, arcfour (RFC 8758) |
| MAC (only with non-AEAD ciphers) | hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com |
RFC 6668 (the HMACs), OpenSSH PROTOCOL (the -etm order) |
hmac-md5, hmac-sha1, non-etm forms |
| Private key file | OPENSSH PRIVATE KEY, passphrase through bcrypt_pbkdf |
OpenSSH PROTOCOL.key |
old PEM files (passphrase through one MD5) |
| Host key in DNS | SSHFP records, with DNSSEC | RFC 4255, RFC 6594 | SSHFP without DNSSEC |
The protocol itself: RFC 4251 (architecture), RFC 4252 (user login), RFC 4253 (transport), RFC 4254 (channels); RFC 4716 (public key file).
Implementations¶
| Implementation | Where you meet it | Its cryptography |
|---|---|---|
| OpenSSH | Linux, BSD, macOS, Windows 10+, most servers | own Ed25519, ChaCha20-Poly1305, Curve25519, sntrup761 and ML-KEM (formally verified code from libcrux); AES, RSA and ECDSA from OpenSSL or LibreSSL |
| PuTTY | Windows desktops | its own code for everything |
| Dropbear | routers, small devices | its own code with LibTomCrypt |
| libssh / libssh2 | programs that speak SSH (libssh2 in curl and libgit2) | OpenSSL, mbedTLS or libgcrypt |
Go golang.org/x/crypto/ssh |
Go servers and tools | Go's standard library |
| Paramiko, AsyncSSH | Python scripts (Ansible uses Paramiko or OpenSSH) | the cryptography package (OpenSSL) |
| Apache MINA SSHD, russh | Java and Rust | Java's JCA / BouncyCastle; RustCrypto |
To see what a server offers and get a graded report, use ssh-audit (ssh-audit server.example.com).
Common mistakes, and what they cost¶
| Mistake | What happened |
|---|---|
| Typing "yes" to a changed host key warning without checking | This is exactly what a man-in-the-middle attack looks like. Check the new fingerprint with the admin first. GitHub really did change its RSA host key on 24 March 2023, after it leaked; it announced the new fingerprints. |
Agent forwarding (ssh -A) to a server you don't fully trust |
Anyone with root there can use your agent to log in as you while you are connected. Use ssh -J jumphost target (ProxyJump) instead. |
| A private key with no passphrase on a laptop | A stolen laptop or backup is a stolen login. Use a passphrase plus the agent, or a FIDO key (-t ed25519-sk). |
| Password login left on for an internet server | Bots try passwords all day. Turn it off once keys work. |
| ECDSA with a weak nonce | Each ECDSA signature needs a fresh secret random number. PuTTY 0.68–0.80 made biased ones for P-521 keys, so about 60 signatures gave away the private key (CVE-2024-31497, fixed in 0.81, April 2024). Ed25519 can't fail this way. |
| Weak randomness when making keys | Debian's OpenSSL (2006–2008) seeded its generator with little more than the process ID, so every key made there was one of about 32,768 per type and size (CVE-2008-0166). |
Copying authorized_keys everywhere and never removing old keys |
People leave; their keys keep working. Keep a list, or use short-lived certificates. |
| Not updating the server | Terrapin (CVE-2023-48795, Dec 2023) let an attacker quietly delete a message at the start of the encrypted channel: fixed by "strict key exchange" in OpenSSH 9.6, on both sides. regreSSHion (CVE-2024-6387) gave root to anyone on OpenSSH 8.5p1–9.7p1 on glibc Linux: fixed in 9.8 (July 2024). The xz backdoor (CVE-2024-3094, March 2024) reached sshd through a compression library on some Linux distributions and was caught just before stable releases. |
Best practices¶
Keys: Ed25519 with a passphrase, or ed25519-sk on a security key. One key per device, so a lost device costs one key. RSA (3072 bits or more) only for systems that predate Ed25519 (OpenSSH before 6.5, 2014).
Server (/etc/ssh/sshd_config, or a file in sshd_config.d/). Test before you restart, and keep a second session open while you do:
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password # or: no
AuthenticationMethods publickey
AllowUsers alice bob
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no # unless you need tunnels
KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
sudo sshd -t # syntax check: silent when fine
sudo sshd -T | grep -E 'passwordauth|permitroot|kexalg' # what it will really use
On OpenSSH 9.x, drop mlkem768x25519-sha256 from the list (it came in 9.9; the default in 10.0).
Client (~/.ssh/config):
Host *
IdentitiesOnly yes # offer only the key named for each host
ForwardAgent no
StrictHostKeyChecking ask
UpdateHostKeys yes # accept a server's new key only when its old one vouches for it
Host work
HostName 10.0.0.5
User alice
ProxyJump bastion.example.com
At scale: an SSH certificate authority (TrustedUserCAKeys on servers, @cert-authority in known_hosts) with certificates valid for hours, not keys that live forever; SSHFP records for host keys; logs sent off the server.
Learn it properly¶
- Book, 19.5 SSH: how the handshake works, step by step, and what went wrong in its history
- Ed25519, ECDSA, RSA, ECDH, ML-KEM, ChaCha, AES: each algorithm in depth
- FIDO2: the security keys behind
-skkeys
Now and next¶
Last checked: 4 November 2026.
- Post-quantum key exchange is the default. OpenSSH has used a hybrid by default since 9.0 (2022,
sntrup761x25519) andmlkem768x25519-sha256since 10.0 (April 2025). A hybrid stays safe if either half holds. It protects today's sessions from being recorded now and decrypted by a future quantum computer. It needs no new keys. - Post-quantum signatures are arriving. OpenSSH 10.4 added an experimental ML-DSA-44 + Ed25519 key type, and 10.6 (October 2026) renamed it
ssh-mldsa44-ed25519and turned it on, last in the preference list (ssh-keygen). Signatures matter less urgently than key exchange: a future quantum computer cannot go back and log in to a past session. - Standards work is in the IETF SSH Maintenance (sshm) working group: SSH certificates (draft-ietf-sshm-cert) and composite post-quantum signatures (draft-ietf-sshm-composite-sigs).
- To watch: the OpenSSH release notes and the sshm working group.