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.
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