~/blog/-blog-ssh-keys-explained-
blog · Security

SSH Keys Explained: How They Work, How to Generate Them, and Why Passwords Are Not Enough

What SSH keys actually are, how public-key cryptography makes them secure, how to generate and add them to GitHub and remote servers, and the mistakes that expose you.

last updated · June 20, 2026by @vultio

The core idea: you prove who you are without ever sending a secret

With password authentication, you send a secret across the network. Even over an encrypted channel, the server receives your password, checks it, and if the server is ever compromised or the password is weak, you lose access to everything. SSH key authentication never sends a secret anywhere. The private key stays on your machine. The server only ever sees the public key.

During login, the server sends you a random challenge encrypted with your public key. Only your private key can decrypt it. You return proof that you decrypted it, and the server grants access. The private key never leaves your machine. There is nothing for an attacker to steal from the server.

Generating an SSH key pair

The standard tool is ssh-keygen, available on macOS, Linux, and Windows (Git Bash or Windows Subsystem for Linux). The current recommendation is Ed25519 — it is faster, produces shorter keys, and is considered more secure than older RSA-2048.

# Generate a new Ed25519 key pair
ssh-keygen -t ed25519 -C "your-email@example.com"

# The -C comment identifies the key in authorized_keys lists
# and in GitHub's key list. Use your email or hostname.

# What happens:
# Generating public/private ed25519 key pair.
# Enter file in which to save the key (~/.ssh/id_ed25519):
#   → press Enter to accept the default location
# Enter passphrase (empty for no passphrase):
#   → add a passphrase (recommended — explained below)
# Enter same passphrase again:

# Result: two files
~/.ssh/id_ed25519       # private key — never share this
~/.ssh/id_ed25519.pub   # public key — safe to share anywhere

# If you need RSA for compatibility with older systems:
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

Always add a passphrase. If your private key file is stolen (laptop theft, accidental commit, compromised backup), the passphrase is the only thing protecting it. You will only be prompted for the passphrase once per session when using ssh-agent, so the ergonomic cost is minimal.

Adding your public key to a remote server

The remote server keeps a list of authorized public keys in ~/.ssh/authorized_keys. Any client that can prove it holds the corresponding private key gets access. Thessh-copy-id command does this automatically.

# Copy your public key to a remote server (uses password auth once)
ssh-copy-id user@server.example.com

# Specify a key if you have multiple
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com

# If ssh-copy-id is not available, do it manually
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Verify it worked (should log in without a password prompt)
ssh user@server.example.com

# Correct permissions on the server — SSH will refuse to work otherwise
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Adding an SSH key to GitHub

# Copy your public key to the clipboard
# macOS
pbcopy < ~/.ssh/id_ed25519.pub

# Linux
cat ~/.ssh/id_ed25519.pub | xclip -selection clipboard
# or just print it and copy manually
cat ~/.ssh/id_ed25519.pub

# Then go to:
# GitHub → Settings → SSH and GPG keys → New SSH key
# Paste the contents of your .pub file and save.

# Test the connection
ssh -T git@github.com
# Expected: Hi username! You've successfully authenticated...

# Clone over SSH (instead of HTTPS)
git clone git@github.com:user/repo.git

# Switch an existing HTTPS remote to SSH
git remote set-url origin git@github.com:user/repo.git

Using ssh-agent to avoid typing your passphrase repeatedly

ssh-agent is a background process that holds your decrypted private key in memory for the duration of your session. You unlock the key once with your passphrase, and every subsequent SSH operation uses the cached key silently.

# Start ssh-agent (usually already running on macOS and most Linux desktops)
eval "$(ssh-agent -s)"

# Add your key — you will be prompted for the passphrase once
ssh-add ~/.ssh/id_ed25519

# On macOS, add to keychain so it persists across reboots
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

# macOS: also add to ~/.ssh/config to use keychain automatically
Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519

# List keys currently in the agent
ssh-add -l

# Remove all keys from the agent
ssh-add -D

Managing multiple keys with ~/.ssh/config

When you have keys for different hosts — personal GitHub, work GitHub, staging servers, production servers — SSH config lets you specify which key to use for which host without remembering flags.

# ~/.ssh/config
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal

Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work

Host staging
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/id_ed25519_staging
  Port 2222

Host prod-*
  User ubuntu
  IdentityFile ~/.ssh/id_ed25519_prod
  ForwardAgent no           # never forward agent to production

# Usage:
git clone git@github-work:mycompany/repo.git  # uses work key
ssh staging                                    # connects to 203.0.113.10:2222
ssh prod-web-01                                # matches prod-* pattern

The mistakes that expose your keys

Committing the private key to git is the most common catastrophic mistake. The private key is the file without the .pub extension. Add ~/.ssh/to your global gitignore, and if you ever accidentally commit a private key, rotate it immediately — even after removing it from history, the key should be considered compromised.

Using ForwardAgent yes to untrusted servers is the second major risk. Agent forwarding allows the remote server to use your local SSH agent — meaning anyone with root on that server can authenticate to other servers as you. Only forward the agent to machines you fully control. Never forward to shared servers, CI runners, or cloud instances you do not own.

Keeping old keys around indefinitely. Audit your authorized_keyson servers you manage and remove keys for users who no longer need access. Rotate your own keys if a device is lost, stolen, or decommissioned. A key that is no longer needed is an attack surface with no benefit.

Disabling password authentication on your server

Once your SSH key is set up and tested, disabling password authentication entirely eliminates brute-force attacks against your server. Any internet-facing SSH server receives thousands of automated login attempts per day. With key-only auth, none of them can succeed regardless of how many passwords they try.

# /etc/ssh/sshd_config — edit on the server
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no          # also disable direct root SSH

# After editing, test the config before restarting
sshd -t

# Restart the SSH daemon (keep your current session open as a fallback)
systemctl restart sshd

# CRITICAL: verify you can open a NEW connection before closing the current one
# If you misconfigure sshd, you can lock yourself out permanently