Based on real troubleshooting - every step explains what you're doing and why.
SSH key authentication is a lock-and-key system between two machines:
| Concept | Analogy | File |
|---|---|---|
| Private Key | Your physical house key (never share it) | id_ed25519 on the Client |
| Public Key | A lock you install on a door | id_ed25519.pub → copied to the Host |
| Host | The remote PC you want to control | Stores the "lock" (public key) |
| Client | Your laptop/controller | Holds the "key" (private key) |
The flow:
- Client says: "I have this key, let me in."
- Host checks its list of approved locks.
- If the key fits a lock → access granted, no password needed.
- If no match → falls back to asking for a password.
- The Host doesn't have your public key in the right file → password prompt
- The file permissions are too loose → SSH ignores the key for security → password prompt
- You used the wrong username → the account doesn't exist or has no password set → denied
- You regenerated keys but forgot to update the Host → lock and key no longer match → denied
Run these on the device you connect FROM. Regular PowerShell is fine.
ssh-keygen -t ed25519- Press Enter to accept the default save location (
C:\Users\YourName\.ssh\id_ed25519) - Press Enter twice to skip passphrase (or set one for extra security)
This creates two files:
| File | What it is | Share it? |
|---|---|---|
~/.ssh/id_ed25519 |
Your private key | ❌ NEVER |
~/.ssh/id_ed25519.pub |
Your public key | ✅ Yes, copy to Host |
cat ~/.ssh/id_ed25519.pubCopy the entire output. It looks like:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... yourname@YourPC
⚠️ WARNING - The Key Overwrite Trap If you ever runssh-keygenagain and overwrite the key, the fingerprint changes. The Host will no longer recognize you. You must re-copy the new public key to the Host every time you regenerate.
Save this output somewhere. You will paste it on the Host in Part 2.
Run ALL of these in an Administrator PowerShell window on the target machine.
whoamiExpected output: COMPUTERNAME\hp or COMPUTERNAME\john, etc.
🔑 WHY THIS MATTERS: The part after the
\is your actual SSH username. This is often NOT your display name.
What you see on screen What SSH actually needs Display Name: "Avitesh" ❌ Won't work Microsoft email: avitesh@gmail.com ❌ Won't work Folder: C:\Users\HP✅ Username is hpWrite this username down. You will use it to connect.
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0Then start it and make it run on every boot:
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'Checklist:
- Command completed without errors
-
Get-Service sshdshows Status: Running
🔑 WHY
administrators_authorized_keysinstead ofauthorized_keys?Windows SSH has a special rule: if your account is in the Administrators group (most home PCs), it ignores the normal user-level key file (
C:\Users\hp\.ssh\authorized_keys) and ONLY checks a global file atC:\ProgramData\ssh\administrators_authorized_keys.This is controlled by the
Match Group administratorsblock insshd_config. If you're an admin, your key must go in ProgramData.
Copy and paste this entire block at once:
# Create the directory (if it doesn't exist) and the key file
New-Item -ItemType Directory -Force -Path "C:\ProgramData\ssh"
New-Item -ItemType File -Force -Path "C:\ProgramData\ssh\administrators_authorized_keys"
# Lock down permissions - ONLY System and Administrators can touch this file
$path = "C:\ProgramData\ssh\administrators_authorized_keys"
$acl = Get-Acl $path
$acl.SetAccessRuleProtection($true, $false) # Remove inherited permissions
$acl.Access | ForEach-Object { $acl.RemoveAccessRule($_) } # Clear all existing rules
$admins = New-Object System.Security.AccessControl.FileSystemAccessRule("Administrators","FullControl","Allow")
$system = New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM","FullControl","Allow")
$acl.AddAccessRule($admins)
$acl.AddAccessRule($system)
Set-Acl $path $acl🔑 WHY PERMISSIONS MATTER: The
sshdservice runs asLOCAL SYSTEM. If the SYSTEM account can't read the key file, the service can't verify your key, so it falls back to a password. If ANY other user (like "Everyone") has access, SSH considers the file "compromised" and ignores it entirely.
Checklist:
- File exists at
C:\ProgramData\ssh\administrators_authorized_keys - Running
icacls "C:\ProgramData\ssh\administrators_authorized_keys"shows ONLYNT AUTHORITY\SYSTEMandBUILTIN\Administrators
notepad "C:\ProgramData\ssh\administrators_authorized_keys"- Paste the public key you copied from the Client (the
ssh-ed25519 AAAAC3...string) - Save and close Notepad
Verify it's correct:
cat "C:\ProgramData\ssh\administrators_authorized_keys"Checklist:
- The output starts with
ssh-ed25519 - The output is a single line (no line breaks in the middle of the key)
- The fingerprint matches what
cat ~/.ssh/id_ed25519.pubshows on the Client
notepad "C:\ProgramData\ssh\sshd_config"🔑 HOW
sshd_configWORKS:
- Lines starting with
#are comments (ignored by SSH)- Settings are read top-to-bottom
- The
Match Groupblock at the bottom overrides everything above it for users in that group
Find and set these lines (remove the # if present):
| Line to find | Change to | Why |
|---|---|---|
#PubkeyAuthentication yes |
PubkeyAuthentication yes |
Enables key-based login. Commented = relies on default, which can be unreliable on Windows. |
AuthorizedKeysFile .ssh/authorized_keys |
#AuthorizedKeysFile .ssh/authorized_keys |
Comment this OUT. For admin accounts, the Match Group block at the bottom handles this. Leaving it active can cause confusion. |
#PasswordAuthentication yes |
PasswordAuthentication yes |
Keep this ON as a backup until keys work. Once confirmed, you can set to no to disable password login entirely. |
At the very BOTTOM of the file, ensure these lines exist and are NOT commented:
Match Group administrators
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
⚠️ THE OVERRIDE RULE: OnceMatch Group administratorsis active, the SSH server says: "For any user in the Administrators group, ignore ALL other AuthorizedKeysFile settings and ONLY look at__PROGRAMDATA__/ssh/administrators_authorized_keys."This means:
- ✅ Key in
C:\ProgramData\ssh\administrators_authorized_keys→ Works- ❌ Key in
C:\Users\hp\.ssh\authorized_keys→ Completely ignored- ❌ Key in
C:\Users\hp\.ssh\administrators_authorized_keys→ Wrong location, wrong filename for this folder
PubkeyAuthentication yes
#AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication yes
Subsystem sftp sftp-server.exe
Match Group administrators
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
Save and close.
Restart-Service sshdThe Host is now ready.
ssh hp@100.103.129.41Replace hp with the username from whoami and the IP with your Host's IP (Tailscale IP, LAN IP, etc).
If it works, you'll see something like:
Microsoft Windows [Version 10.0.26200.8037]
(c) Microsoft Corporation. All rights reserved.
hp@LAPTOP-4Q6FRMSD C:\Users\HP>
No password prompt = success! 🎉
On your Client, create/edit this file:
notepad ~/.ssh/configPaste this:
Host remote-pc
HostName 100.103.129.41
User hp
IdentityFile ~/.ssh/id_ed25519
Now you can connect with just:
ssh remote-pcIf you get "Permission denied" after following everything above, work through this list:
On Client:
cat ~/.ssh/id_ed25519.pubOn Host:
cat C:\ProgramData\ssh\administrators_authorized_keys- Both outputs are identical (same key string, same fingerprint)
- You haven't run
ssh-keygenagain since pasting the key
On Host:
whoami- You are using the name after
\in yoursshcommand (e.g.,hp, notAvitesh)
On Host (Admin PowerShell):
icacls "C:\ProgramData\ssh\administrators_authorized_keys"- Shows ONLY
NT AUTHORITY\SYSTEM:(F)andBUILTIN\Administrators:(F) - Does NOT show
Everyone,Users, or any individual username
If permissions are wrong, fix with:
icacls "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "NT AUTHORITY\SYSTEM:(F)" /grant "BUILTIN\Administrators:(F)"On Host:
cat C:\ProgramData\ssh\sshd_config | Select-String -Pattern "PubkeyAuthentication|AuthorizedKeysFile|Match Group|PasswordAuthentication" | Where-Object { $_.Line -notmatch "^#" }-
PubkeyAuthentication yesappears -
Match Group administratorsappears -
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keysappears (indented under Match Group)
On Client:
ssh -v hp@100.103.129.41Look for these lines in the output:
| Line you see | What it means |
|---|---|
Offering public key: ... ED25519 SHA256:xxx |
✅ Client is sending your key |
Server accepts key |
✅ Host recognized the key - you're in! |
Authentications that can continue: password (after offering key) |
❌ Host rejected the key - check permissions & file content |
On Host: Open Event Viewer → Applications and Services Logs → OpenSSH → Operational
Look for errors at the exact time you attempted to connect. Common messages:
- "Authentication refused: bad ownership or modes" → Fix permissions (Step 3)
- "Could not open authorized keys" → File doesn't exist or wrong path
- "User not found" → Wrong username
| Item | Admin User | Standard User |
|---|---|---|
| Key file location on Host | C:\ProgramData\ssh\administrators_authorized_keys |
C:\Users\USERNAME\.ssh\authorized_keys |
Match Group administrators in config |
Uncommented (active) | Commented out with # |
| Permissions on key file | SYSTEM + Administrators only | SYSTEM + that specific user only |
| Connect command | ssh hp@IP |
ssh username@IP |
| Pitfall | Why It Happens | How to Avoid |
|---|---|---|
| Wrong username | Display name ≠ account name | Always run whoami on Host first |
| Key mismatch after regeneration | ssh-keygen overwrites old key |
Update the Host's key file every time you regenerate |
| Key in wrong file/folder | Admin accounts ignore user folders | Admin → ProgramData; Standard → User folder |
| Loose permissions | Any extra user = SSH ignores the file | Strip inheritance, allow only SYSTEM + Admins |
PubkeyAuthentication commented out |
Default should be yes, but Windows is inconsistent | Always explicitly uncomment it |
AuthorizedKeysFile conflict |
Top-level setting fights with Match Group block |
Comment out the top-level one for admin accounts |