Key-Based SSH Login — Cross-OS FAQ
A practical guide to setting up passwordless SSH login between any two machines, where any OS (macOS, Linux, Windows) can be the server or the client.
The whole process is four tasks:
- Create a key pair (on the client) — identical on every OS.
- Put the public key on the server — depends only on the server’s OS.
- Configure the client with an
~/.ssh/configalias — identical on every OS. - (Optional) Load the key into an agent so a passphrase is typed once.
Do those and ssh myserver logs you in with no password.
Concepts
Q: What is “the server” vs “the client”?
- Client = the machine you connect from (where you type
ssh). - Server = the machine you connect to (it runs the SSH server,
sshd).
Either role can be any OS. A Windows laptop can SSH into a Mac; a Mac can SSH into a Linux box; a Linux box can SSH into Windows. The steps below are labelled by role, so mix and match.
Q: How does key-based login actually work?
You generate a key pair: a private key (stays secret on the client,
never leaves it) and a public key (safe to share). You place the public key
in the server’s authorized_keys. At login, the server challenges the client to
prove it holds the matching private key — no password crosses the network.
Q: What has to be true on each machine before I start?
- Client: an SSH client. Built in on macOS, Linux, and Windows 10/11
(
sshon the PATH). - Server: an SSH server installed and running:
- Linux:
openssh-server(usually preinstalled;sudo apt install openssh-serverif not). - macOS: enable Remote Login (see below) — nothing to install.
- Windows: install the OpenSSH Server optional feature and start
sshd(see the Windows server section).
- Linux:
Task 1 — Create a key pair (on the CLIENT)
Q: How do I create a key? (same command on macOS, Linux, and Windows)
Open a terminal (macOS/Linux) or PowerShell (Windows) and run:
ssh-keygen -t ed25519 -C "you@your-machine"
- Press Enter to accept the default location.
- Enter a passphrase (recommended) or leave it empty for fully unattended login.
-t ed25519is the modern, recommended key type. If you must interoperate with very old servers that don’t support it, use-t rsa -b 4096instead.
This creates two files. Never share or copy the private one.
Q: Where are my keys stored?
| OS | Private key | Public key (.pub) |
|---|---|---|
| macOS | ~/.ssh/id_ed25519 |
~/.ssh/id_ed25519.pub |
| Linux | ~/.ssh/id_ed25519 |
~/.ssh/id_ed25519.pub |
| Windows | C:\Users\<you>\.ssh\id_ed25519 |
C:\Users\<you>\.ssh\id_ed25519.pub |
On Windows, %USERPROFILE%\.ssh\ is the same folder.
Q: What does a public key look like?
One single line, e.g.:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... you@your-machine
That entire line is what goes into the server’s authorized_keys.
Task 2 — Put the PUBLIC key on the SERVER
Pick the section that matches the server’s OS. The goal is always the same:
add your public-key line to the right authorized_keys file with correct
permissions.
Server is Linux or macOS
On a macOS server, first enable Remote Login:
- System Settings → General → Sharing → Remote Login → On, or from a terminal:
sudo systemsetup -setremotelogin on
Then add the key. The target file is ~/.ssh/authorized_keys in the
server user’s home directory, and permissions must be strict.
Easiest — from a macOS/Linux client, use ssh-copy-id (it creates the
folder, appends the key, and fixes permissions for you):
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
From a Windows client (no ssh-copy-id), pipe the key over in PowerShell:
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Manual method (any client): open the .pub file, copy the single line, then
on the server:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA...your-key... you@your-machine" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Permissions matter.
~/.sshmust be700andauthorized_keys600, owned by the user. If the home directory is group- or world-writable, sshd silently refuses the key. This is the #1 cause of “Permission denied (publickey)”.
Server is Windows
First, enable the OpenSSH Server (elevated PowerShell, one time):
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Set-Service -Name sshd -StartupType Automatic
Start-Service sshd
Then add the key — the file location depends on the server account type:
- Standard (non-admin) user:
C:\Users\<user>\.ssh\authorized_keys - Administrator account:
C:\ProgramData\ssh\administrators_authorized_keys— a single shared file for all admins. This is the Windows gotcha that trips up almost everyone: keys in the user’s own.ssh\authorized_keysare ignored for admin accounts.
For a standard user, from a macOS/Linux client:
ssh-copy-id user@windows-server
For an administrator account, paste the key into the shared file and fix its ACL (run on the Windows server in elevated PowerShell):
# Paste your public-key line into the file (replace the key text):
Add-Content -Path C:\ProgramData\ssh\administrators_authorized_keys -Value "ssh-ed25519 AAAA...your-key... you@your-machine"
# Lock the file down or sshd will refuse it:
icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
Two Windows-server notes:
- The permissions/ACL step is mandatory — a too-permissive
administrators_authorized_keysis silently rejected.- To make PowerShell the shell you land in on connect:
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
Task 3 — Configure the CLIENT for auto-login
Q: How do I set up ~/.ssh/config so I can just type a short name?
Create or edit the SSH client config file and add a Host block per server.
The format is identical on every OS; only the file location differs.
| Client OS | Config file path |
|---|---|
| macOS | ~/.ssh/config |
| Linux | ~/.ssh/config |
| Windows | C:\Users\<you>\.ssh\config |
Example config:
Host win11
HostName 192.168.1.50
User kishore
IdentityFile ~/.ssh/id_ed25519
Port 22
Host macmini
HostName 192.168.1.60
User kishore
IdentityFile ~/.ssh/id_ed25519
# Apply keepalives to every host
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
Now instead of ssh -i ~/.ssh/id_ed25519 kishore@192.168.1.50 you just run:
ssh win11
Q: Anything special about the config on Windows?
~resolves toC:\Users\<you>, soIdentityFile ~/.ssh/id_ed25519works.- If you write a full path, use forward slashes (
C:/Users/you/.ssh/id_ed25519) or escaped backslashes to avoid quoting issues.
Q: What do the fields mean?
Host— the short alias you type.HostName— the actual IP or DNS name.User— the account on the server.IdentityFile— the private key to authenticate with.Port— only needed if the server isn’t on 22.
Task 4 (Optional) — Use ssh-agent for passphrase-protected keys
If you gave your key a passphrase (recommended), an agent holds the decrypted key so you type the passphrase once per session instead of every login.
macOS — store the passphrase in the Keychain so it’s remembered across reboots:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Add to the top of ~/.ssh/config:
Host *
UseKeychain yes
AddKeysToAgent yes
Linux — start the agent and add the key:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
(Most desktop environments start an agent automatically; AddKeysToAgent yes
in config loads keys on first use.)
Windows — enable and start the built-in agent service (elevated PowerShell, one time), then add the key:
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
Permissions quick reference
Wrong permissions are the most common reason keys are ignored.
| Location | macOS / Linux | Windows |
|---|---|---|
.ssh folder |
700 |
User has full control; not open to others |
authorized_keys (standard user) |
600 |
User full control |
Admin administrators_authorized_keys |
n/a | icacls ... /inheritance:r /grant Administrators:F /grant SYSTEM:F |
| Private key on client | 600 |
Only your account should have access |
| Home directory | not group/world-writable | — |
Troubleshooting FAQ
Q: I still get asked for a password.
The server didn’t accept the key, so it fell back to password auth. Check, in order:
- Public key is actually in the server’s
authorized_keys(and, on a Windows admin account, inadministrators_authorized_keys, not the user file). - Server-side permissions (
~/.ssh= 700,authorized_keys= 600; Windows ACL set). - The client is offering the right key —
IdentityFilepoints at the private key.
Q: “Permission denied (publickey).”
Same causes as above, plus: wrong User, wrong key, or the server has public-key
auth disabled. Run a verbose connection to see exactly which key is tried and why
it’s rejected:
ssh -v user@server # add more v's (-vv, -vvv) for detail
Q: “Unprotected private key file” / “bad permissions” on the client.
Your private key is readable by others. Tighten it:
- macOS/Linux:
chmod 600 ~/.ssh/id_ed25519 - Windows: remove inherited permissions and grant only your account (via file
Properties → Security, or
icacls).
Q: Windows server — my key works for a standard user but not for my admin account.
You put it in the wrong file. Admin accounts read
C:\ProgramData\ssh\administrators_authorized_keys, and that file needs the
icacls lockdown shown above.
Q: macOS server refuses all connections.
Remote Login isn’t enabled. Turn it on under System Settings → General → Sharing → Remote Login.
Q: How do I confirm the config alias resolves correctly?
ssh -G win11 # prints the effective settings for that alias
Optional hardening — disable password login once keys work
After you’ve confirmed key login works, you can turn off password auth on the server so only keys are accepted.
- Linux/macOS: edit
/etc/ssh/sshd_config, setPasswordAuthentication no, then reload:sudo systemctl restart sshd(Linux) or toggle Remote Login off/on (macOS). - Windows: edit
C:\ProgramData\ssh\sshd_config, setPasswordAuthentication no, thenRestart-Service sshd.
Don’t do this until at least one key login succeeds, or you can lock yourself out — keep a second session open while you test.