Key-Based Authentication

Chapter 3 — Key-Based Authentication

Typing a password every time you connect is slow, and passwords can be guessed, leaked, or brute-forced. Key-based authentication replaces the password with a cryptographic key pair — faster to use, significantly harder to attack, and the standard approach for any server you manage regularly. Once it's set up, connecting feels like magic: you type ssh philip@server and you're in, no password prompt at all.

How it works in one sentence: you generate a key pair, put the public key on the server, and SSH proves your identity by signing a challenge with your private key — the server verifies the signature without your private key ever leaving your machine.

The Setup Flow

1
Generate a key pair on your local machine
Creates two files: ~/.ssh/id_ed25519 (private) and ~/.ssh/id_ed25519.pub (public). Runs on your machine only — nothing touches the server yet.
2
Copy your public key to the server
Use ssh-copy-id to append your public key to ~/.ssh/authorized_keys on the server. This step still uses your password — it's the last time you'll need it.
3
Test key-based login
Connect with ssh philip@server — if it works without asking for a password, keys are set up correctly.
4
Optionally: disable password authentication
Once keys work, disable password login on the server entirely. This eliminates brute-force attacks. Only do this after confirming key login works from a second terminal.

Step 1 — Generate Your Key Pair

Run this on your local machine — the one you're connecting from. Use Ed25519 unless you specifically need RSA for an older server.

philip@debian — generate an Ed25519 key pair
philip@debian:~$ ssh-keygen -t ed25519 -C "philip@debian" Generating public/private ed25519 key pair. Enter file in which to save the key (/home/philip/.ssh/id_ed25519): press Enter to accept default Enter passphrase (empty for no passphrase): recommended: enter a passphrase Enter same passphrase again: Your identification has been saved in /home/philip/.ssh/id_ed25519 Your public key has been saved in /home/philip/.ssh/id_ed25519.pub The key fingerprint is: SHA256:xyz789ABCdef123GHI456jklMNO philip@debian The key's randomart image is: +--[ED25519 256]--+ | .o+ | | . o.o | | . + + . | +----[SHA256]-----+

What the flags mean

  • -t ed25519 — use the Ed25519 algorithm (modern, fast, recommended).
  • -C "philip@debian" — a comment appended to the public key. Helps identify which machine a key came from when you have several. Use anything descriptive: your email, hostname, or purpose.

Should I set a passphrase?

Yes — and here's why. Your private key is just a file. If someone gets access to your machine (stolen laptop, compromised account), they can use your private key to connect to every server it's authorised on. A passphrase encrypts the key file itself, so stealing the file alone is not enough.

The passphrase is only ever typed on your local machine — never sent over the network. Use the SSH agent (below) so you only type it once per login session, not on every connection.

Public Key — share freely
~/.ssh/id_ed25519.pub
Copy this to every server you want to access. It's safe to email, paste, or put in a GitHub profile. Begins with ssh-ed25519 AAAA...
Private Key — never share
~/.ssh/id_ed25519
Stays on your machine only. If you ever paste this somewhere, treat it as compromised and generate a new pair immediately. Begins with -----BEGIN OPENSSH PRIVATE KEY-----

Step 2 — Copy Your Public Key to the Server

The easy way: ssh-copy-id

ssh-copy-id handles everything — it connects to the server, creates the ~/.ssh/ directory if needed, appends your public key to authorized_keys, and sets the correct permissions. This is one of the most useful commands in the SSH toolkit.

philip@debian — copy public key to server
philip@debian:~$ ssh-copy-id philip@192.168.1.100 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/philip/.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 philip@192.168.1.100's password: ← last time you need the password Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'philip@192.168.1.100'" and check to make sure that only the key(s) you wanted were added.

If you need to specify a key file or a non-standard port:

philip@debian — ssh-copy-id with options
# Specify a key file explicitly philip@debian:~$ ssh-copy-id -i ~/.ssh/id_ed25519.pub philip@server # Server on a non-standard port philip@debian:~$ ssh-copy-id -p 2222 philip@server

The manual way — if ssh-copy-id isn't available

On Windows (before OpenSSH was built in) or on minimal systems, ssh-copy-id may not be present. Do it manually:

philip@debian — manual key copy (one-liner)
# Pipe the public key into the server's authorized_keys in one command philip@debian:~$ cat ~/.ssh/id_ed25519.pub | ssh philip@server \ "mkdir -p ~/.ssh && chmod 700 ~/.ssh && \ cat >> ~/.ssh/authorized_keys && \ chmod 600 ~/.ssh/authorized_keys" philip@server's password:

From Windows PowerShell

PowerShell — copy public key to Linux server
PS C:\Users\Philip> type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh philip@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Step 3 — Test Key Login

philip@debian — test key-based login
philip@debian:~$ ssh philip@192.168.1.100 (if key has a passphrase, you're prompted for it once — not the server password) Enter passphrase for key '/home/philip/.ssh/id_ed25519': Welcome to Debian GNU/Linux 12 (bookworm) philip@server:~$ ← in without typing the server password

If it still asks for the server password rather than the key passphrase, see the troubleshooting section at the end of this chapter.

The SSH Agent — Type Your Passphrase Once

With a passphrase-protected key, you'd need to type the passphrase every time you connect. The SSH agent solves this: it holds your decrypted key in memory for the duration of your login session, so you type the passphrase once and SSH silently uses it for every subsequent connection.

philip@debian — using the SSH agent
# Start the agent (usually already running on a Linux desktop) philip@debian:~$ eval "$(ssh-agent -s)" Agent pid 12345 # Add your key — type the passphrase once here philip@debian:~$ ssh-add ~/.ssh/id_ed25519 Enter passphrase for /home/philip/.ssh/id_ed25519: Identity added: /home/philip/.ssh/id_ed25519 (philip@debian) # All subsequent connections use the cached key — no passphrase prompt philip@debian:~$ ssh philip@server philip@server:~$ ← straight in, no prompt # List keys currently held by the agent philip@debian:~$ ssh-add -l 256 SHA256:xyz789... /home/philip/.ssh/id_ed25519 (ED25519)
On most Linux desktops (GNOME, KDE, XFCE) the SSH agent starts automatically when you log in and a keyring prompt handles the passphrase transparently. On macOS, keys are stored in the system keychain. On Windows, enable the "OpenSSH Authentication Agent" service in Services and set it to start automatically.

What authorized_keys Looks Like

The authorized_keys file on the server contains one public key per line. Multiple keys means multiple machines can connect — useful if you want both your laptop and desktop to access the same server.

# ~/.ssh/authorized_keys on the SERVER # One public key per line — each grants access from the machine that holds the matching private key ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... philip@debian ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... philip@windows-laptop ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... philip@work-machine

You can also prefix a key with options to restrict what it can do:

# Restrict a key to only run one specific command (useful for automation) command="backup.sh",no-pty,no-agent-forwarding ssh-ed25519 AAAAC3... backup@cronjob # Restrict to connections from a specific IP only from="192.168.1.50" ssh-ed25519 AAAAC3... philip@trusted-machine

Critical: File Permissions

SSH is paranoid about permissions — intentionally. If the ~/.ssh directory or authorized_keys file is writable by anyone other than you, SSH refuses to use them. This prevents another user on the same system from injecting their own key into your file.

File / DirectoryRequired PermissionOwnerWhy
~/.ssh/ 700 you Only you can read or write the directory
~/.ssh/authorized_keys 600 you Only you can read or write the key list
~/.ssh/id_ed25519 600 you Private key — only you should ever read it
~/.ssh/id_ed25519.pub 644 you Public key — readable by others, no harm
~/.ssh/config 600 you Config file — contains hostnames and settings
~/.ssh/ group/world writable 755 or 777 SSH will refuse to use authorized_keys entirely
philip@server — fix permissions if needed
philip@server:~$ chmod 700 ~/.ssh philip@server:~$ chmod 600 ~/.ssh/authorized_keys philip@server:~$ chmod 600 ~/.ssh/id_ed25519 # Verify philip@server:~$ ls -la ~/.ssh/ drwx------ 2 philip philip 4096 Jun 17 20:00 . -rw------- 1 philip philip 571 Jun 17 20:00 authorized_keys -rw------- 1 philip philip 411 Jun 17 20:00 id_ed25519 -rw-r--r-- 1 philip philip 96 Jun 17 20:00 id_ed25519.pub

Step 4 — Disable Password Authentication (Optional but Recommended)

Once key-based login works, disabling password authentication closes the door on brute-force attacks completely. An attacker would need your private key file — guessing passwords becomes irrelevant.

Confirm key login works from a second terminal before doing this. Open a new terminal window and verify you can log in with your key. Only then edit sshd_config in your existing session. If you disable password auth and your key doesn't work, you'll be locked out.
philip@server — disable password authentication
philip@server:~$ sudo vi /etc/ssh/sshd_config

Find and set these three lines (search with /PasswordAuth in vi):

# /etc/ssh/sshd_config — find these lines and set them as shown PasswordAuthentication no PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys
philip@server — reload SSH daemon
# Check config is valid before reloading philip@server:~$ sudo sshd -t (no output = config is valid) philip@server:~$ sudo systemctl reload sshd # Now test from a NEW terminal — password auth should be rejected philip@debian:~$ ssh -o PreferredAuthentications=password philip@server philip@server: Permission denied (publickey). ← correct — password auth is now disabled

Managing Multiple Keys

If you connect to many servers — work, home, cloud, Raspberry Pi — you might want separate key pairs for different purposes. Use -b to name them at generation time:

philip@debian — multiple named key pairs
# Generate a separate key for work servers philip@debian:~$ ssh-keygen -t ed25519 -C "philip-work" -f ~/.ssh/id_work Generating public/private ed25519 key pair. Your identification has been saved in /home/philip/.ssh/id_work Your public key has been saved in /home/philip/.ssh/id_work.pub # Generate a key for a Pi project philip@debian:~$ ssh-keygen -t ed25519 -C "philip-pi" -f ~/.ssh/id_pi # Connect using a specific key philip@debian:~$ ssh -i ~/.ssh/id_work philip@work-server.example.com philip@debian:~$ ssh -i ~/.ssh/id_pi pi@raspberrypi.local
Chapter 4 (SSH config file) makes managing multiple keys much cleaner — you can set which key to use per host automatically, so you never need to remember the -i flag.

Revoking Access — Removing a Key

To stop a machine connecting to a server, remove its public key from authorized_keys on the server. Each key is one line — delete the line for the machine you want to revoke:

philip@server — remove a specific key
philip@server:~$ vi ~/.ssh/authorized_keys Delete the line containing the key you want to revoke, save and exit No service restart needed — authorized_keys is read on every connection attempt

Troubleshooting Key Login

If SSH still asks for the server password after copying your key, work through these checks in order:

philip@debian — debug key authentication
# 1. Check which key SSH is trying to use philip@debian:~$ ssh -v philip@server 2>&1 | grep -E "Offering|Authenticating|refused" debug1: Offering public key: /home/philip/.ssh/id_ed25519 ED25519 debug1: Authentications that can continue: publickey,password # 2. Check permissions on the server philip@server:~$ ls -la ~/.ssh/ .ssh must be 700, authorized_keys must be 600 # 3. Check the public key is actually in authorized_keys philip@server:~$ cat ~/.ssh/authorized_keys Should contain a line starting with ssh-ed25519 # 4. Check the server's sshd_config allows key auth philip@server:~$ grep -E "PubkeyAuth|AuthorizedKeys" /etc/ssh/sshd_config PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys # 5. Check the auth log on the server for the real error philip@server:~$ sudo journalctl -u sshd -n 20 Look for lines mentioning your IP — it will say exactly why the key was rejected

Quick Reference

TaskCommand
Generate Ed25519 key pairssh-keygen -t ed25519 -C "comment"
Generate named key pairssh-keygen -t ed25519 -f ~/.ssh/id_work
Copy public key to serverssh-copy-id philip@server
Copy key, custom portssh-copy-id -p 2222 philip@server
Start SSH agenteval "$(ssh-agent -s)"
Add key to agentssh-add ~/.ssh/id_ed25519
List keys in agentssh-add -l
Remove all keys from agentssh-add -D
View your public keycat ~/.ssh/id_ed25519.pub
Fix .ssh directory permissionschmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
Test with verbose outputssh -v philip@server
Check sshd auth logsudo journalctl -u sshd -n 30
Next — Chapter 4: The SSH Config File. Typing ssh -p 2222 -i ~/.ssh/id_work philip@work-server.example.com every time is tedious. The ~/.ssh/config file lets you define aliases, set per-host options, and manage multiple keys automatically — so ssh work is all you need to type.