SSH Key-Based Authentication

SSH key-based authentication replaces typed passwords with a cryptographic key pair: a private key that never leaves your machine, and a public key you place on the servers you want to access. Because the private key is effectively impossible to guess or forge, key-based login is both more secure and more convenient than password login — nothing to intercept, phish, or brute-force, and no prompt to type on every connection. This lesson covers how the underlying public-key cryptography works, how to generate and deploy keys with ssh-keygen and ssh-copy-id, and the permission rules and mistakes that trip up almost everyone the first time.

Overview / How It Works

SSH (Secure Shell) can authenticate a client to a server in several ways, but the two you’ll actually use are passwords and public-key cryptography. Public-key cryptography uses a mathematically linked pair of keys: a private key and a public key. Anything encrypted or signed with the private key can be verified with the matching public key, but the private key cannot be derived from the public key in any practical amount of time. That asymmetry is the whole trick — you can hand your public key to every server you want to access, and as long as your private key stays secret, only you can prove you own it.

When you generate a key pair with ssh-keygen, two files are created, by default inside ~/.ssh/: a private key (e.g. id_ed25519) and a public key with a .pub suffix (e.g. id_ed25519.pub). The public key is short, plain text, and safe to share — it is literally designed to be copied to other machines. The private key must never leave your computer, never be emailed, never be committed to a repository, and ideally is protected by a passphrase so that even if the file is stolen, it’s useless without that passphrase.

To let a server accept your key, its public half is appended to a file called ~/.ssh/authorized_keys in the account you’re logging into on that server. When you connect, the SSH client tells the server which key it wants to authenticate with. The server checks whether that public key is listed in authorized_keys; if it is, the server sends a challenge that only the holder of the matching private key can answer correctly (this is a challenge-response protocol — the private key itself is never sent over the network, even in encrypted form). If the client signs the challenge correctly, the server grants access. No password is ever transmitted, so there’s nothing for a network eavesdropper or a brute-force script to steal.

Key types

ssh-keygen supports several algorithms. ED25519 is the modern default recommendation: small keys, fast, and cryptographically strong. RSA (with at least 3072 or 4096 bits) is the older, still-widely-supported option, useful for very old servers that don’t understand ED25519. ECDSA exists but is generally not preferred over ED25519 for new keys. Unless you have a specific compatibility requirement, generate ED25519 keys.

Why file permissions matter

sshd, the SSH server daemon, refuses to trust a ~/.ssh directory or authorized_keys file that is writable by anyone other than its owner. This isn’t arbitrary strictness: if other users (or a compromised process running as another user) could write to your authorized_keys file, they could add their own public key and log in as you. The kernel enforces the three permission bits (owner/group/other × read/write/execute) on every file, and sshd additionally checks those bits itself before trusting key-based auth, silently falling back to password auth (or refusing outright) if permissions are too open.

Syntax

ssh-keygen -t <key_type> -f <output_file> -C "<comment>"
  • -t — the key type/algorithm: ed25519 (recommended), rsa, or ecdsa.
  • -b — key size in bits, relevant for RSA (e.g. -b 4096); ED25519 has a fixed size and ignores this flag.
  • -f — output file path for the private key (the public key gets the same name plus .pub).
  • -C — a comment, conventionally user@host, stored in the public key to help identify it later.
  • -i (on ssh/scp) — identity file: which private key to use for a connection.
Command Purpose
ssh-keygen Generate a new key pair
ssh-copy-id Copy your public key to a remote server’s authorized_keys
ssh-agent Background process that holds decrypted private keys in memory
ssh-add Load a private key into a running ssh-agent

Examples

Example 1: Generate a key pair

ssh-keygen -t ed25519 -C "ada@devbox"

Output:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ada/.ssh/id_ed25519): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/ada/.ssh/id_ed25519
Your public key has been saved in /home/ada/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:7QvVvI9k2y+examplefingerprint ada@devbox
The key's randomart image is:
+--[ED25519 256]--+
|    ..+=*=.      |
|   . .+=+o.      |
|    . .+o..      |
|   .   .+  .     |
|  . . . So .     |
| . . . o.o       |
|  . . o.+        |
|   . +.=E        |
|    . *+.        |
+----[SHA256]-----+

Accepting the default path stores the private key at ~/.ssh/id_ed25519 and the public key at ~/.ssh/id_ed25519.pub. Setting a passphrase here is a best practice — it encrypts the private key file at rest, so a copied or stolen file is still useless without it.

Example 2: Deploy the public key with ssh-copy-id

ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@203.0.113.10

Output:

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ada/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
deploy@203.0.113.10's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'deploy@203.0.113.10'"
and check to make sure that only the key(s) you wanted were added.

ssh-copy-id logs in once with your password, then appends your public key to ~/.ssh/authorized_keys on the remote account — creating the directory and file with correct permissions if they don’t already exist. After this runs once, you should never need that password again for this account.

Example 3: Deploy a key manually and verify

If ssh-copy-id isn’t available, you can do the same thing by hand, then test the result:

cat ~/.ssh/id_ed25519.pub | ssh deploy@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
ssh deploy@203.0.113.10

Output:

Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-105-generic x86_64)
Last login: Tue Aug  4 09:12:03 2026 from 198.51.100.7
deploy@web01:~$ 

Piping the public key into a remote shell command lets you build the authorized_keys file without ever copying the private key. The second command connects again — this time with no password prompt at all, proving the key is working.

Example 4: Simplify with an SSH config entry

cat >> ~/.ssh/config <<'EOF'
Host web01
    HostName 203.0.113.10
    User deploy
    IdentityFile ~/.ssh/id_ed25519
    Port 22
EOF
ssh web01

Once this block is in ~/.ssh/config, ssh web01 connects using the right hostname, user, port, and key automatically — no need to remember the full command every time.

How It Works Step by Step

  1. You run ssh deploy@203.0.113.10 (or ssh web01 via config). The client opens a TCP connection to port 22 and negotiates an encrypted transport layer with the server.
  2. The server tells the client which authentication methods it accepts (typically publickey, then password as a fallback).
  3. The client offers a public key it has available (from ~/.ssh/ or loaded in ssh-agent). The server checks whether that exact public key appears in the target account's ~/.ssh/authorized_keys.
  4. If the key is listed, the server sends a random challenge value. The client signs it using the corresponding private key (decrypting it with your passphrase first, if it's encrypted).
  5. The server verifies the signature using the public key it already has on file. A valid signature proves the client holds the private key, without the private key ever crossing the network.
  6. The server grants a shell session over the already-encrypted connection established in step 1.

Common Mistakes

Mistake 1: Permissions on ~/.ssh or authorized_keys are too open

If the directory or file is group- or world-writable, sshd refuses to trust it, silently falling back to password auth:

drwxrwxrwx  2 ada ada 4096 Aug  4 09:00 .ssh
-rw-rw-rw-  1 ada ada  411 Aug  4 09:00 authorized_keys

Fix it by restricting permissions to the owner only:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Mistake 2: Copying the private key to the server instead of the public key

The names look similar, and it's an easy slip that leaks your private key onto a remote machine:

scp ~/.ssh/id_ed25519 "user@<server-ip>:~/.ssh/authorized_keys"

Only the .pub file should ever leave your machine:

scp ~/.ssh/id_ed25519.pub "user@<server-ip>:~/.ssh/id_ed25519.pub"

If you ever do copy a private key to a server or send it anywhere insecure, treat it as compromised: generate a new pair and remove the old public key from every authorized_keys file.

Best Practices

  • Prefer ed25519 keys over RSA or ECDSA for new setups — smaller, faster, and just as strong.
  • Always set a passphrase on the private key; use ssh-agent so you only type it once per session instead of on every connection.
  • Use one key pair per device (laptop, workstation, CI server), not one shared key everywhere — that way losing one device only means revoking one key.
  • Keep ~/.ssh at 700 and private key files at 600; verify with ls -la ~/.ssh after any manual changes.
  • Once key login is confirmed working, disable password authentication in /etc/ssh/sshd_config (PasswordAuthentication no) and restart sshd to close off brute-force password attempts entirely.
  • Use ~/.ssh/config to name hosts and pin the right identity file per server instead of retyping flags.
  • Remove old or unused public keys from authorized_keys promptly when a device is retired or an employee leaves.

Practice Exercises

  1. Generate a new ED25519 key pair with a comment identifying your machine, protect it with a passphrase, then run ls -la ~/.ssh and confirm the private key is 600 and the directory is 700.
  2. Spin up (or use) a remote Linux account you control, deploy your public key to it with ssh-copy-id, and confirm you can ssh in with no password prompt.
  3. Add a Host block to ~/.ssh/config for that server with a short alias, an IdentityFile, and a User, then connect using only ssh <alias>.

Summary

  • SSH key-based authentication uses a private key (kept secret, on your machine) and a public key (shared, placed in a server's authorized_keys).
  • The server never sees your private key; it verifies a signed challenge to prove you hold it.
  • ssh-keygen -t ed25519 -C "user@host" generates a modern key pair.
  • ssh-copy-id is the fast way to install your public key on a remote account; manual deployment via ssh and cat >> works identically.
  • ~/.ssh must be 700 and authorized_keys must be 600, or sshd will refuse to trust them.
  • Never copy or transmit a private key; only the .pub file is meant to be shared.
  • ~/.ssh/config lets you turn long connection commands into short aliases.