Fix SSH 'Permissions for private key are too open' on Windows

beginner๐ŸชŸ Windows2026-03-17| Windows 10/11, OpenSSH (built-in or Git for Windows), PowerShell / CMD

Error Message

Permissions for 'id_rsa' are too open. It is required that your private key files are NOT accessible by others.
#windows#ssh#permission#private-key

The Error

You run an SSH command โ€” connecting to a remote server, cloning a Git repo, or deploying code โ€” and you get this:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/c/Users/YourName/.ssh/id_rsa": bad permissions
Permission denied (publickey).

SSH won't touch the key. Connection dead.

OpenSSH checks file permissions before loading any private key. If another account can read it, SSH considers the key compromised and refuses to proceed. On Linux, one command solves this: chmod 600 ~/.ssh/id_rsa. Windows uses NTFS ACLs instead of Unix permission bits, so the fix looks different.

Why This Happens on Windows

Copying a private key to Windows is usually where things go sideways. Whether it came from a download, a USB drive, or another machine, the file tends to inherit permissions from its parent folder. That means the Users group, SYSTEM, and Administrators may all have read access. OpenSSH sees that and refuses to load the key.

Fix: Using icacls in PowerShell

icacls is the right tool for this. Open PowerShell (no admin rights needed) and run:

# Adjust path if your key is stored elsewhere
$key = "$env:USERPROFILE\.ssh\id_rsa"

# Step 1: Remove all inherited permissions
icacls $key /inheritance:r

# Step 2: Remove broad group permissions
icacls $key /remove "NT AUTHORITY\SYSTEM"
icacls $key /remove "BUILTIN\Administrators"
icacls $key /remove "BUILTIN\Users"

# Step 3: Grant only your user read access
icacls $key /grant:r "${env:USERNAME}:R"

Run these in order. Each strips a layer of permissions โ€” the last adds yours back, read-only.

One-liner version

Prefer one line? Chain them all together:

icacls "$env:USERPROFILE\.ssh\id_rsa" /inheritance:r /remove "NT AUTHORITY\SYSTEM" /remove "BUILTIN\Administrators" /remove "BUILTIN\Users" /grant:r "${env:USERNAME}:R"

Alternative Fix: Using PowerShell ACL Objects

icacls covers most cases. If you're scripting a machine setup or need finer control, you can go lower-level with .NET's System.Security.AccessControl classes directly:

$key = "$env:USERPROFILE\.ssh\id_rsa"

# Load the current ACL
$acl = Get-Acl $key

# Disable inheritance and remove inherited entries
$acl.SetAccessRuleProtection($true, $false)

# Remove all existing access rules
$acl.Access | ForEach-Object { $acl.RemoveAccessRule($_) }

# Add a rule: current user gets Read access only
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $env:USERNAME, "Read", "Allow"
)
$acl.AddAccessRule($rule)

# Apply the updated ACL back to the file
Set-Acl -Path $key -AclObject $acl

Write-Host "Permissions updated for $key"

Fix via GUI (File Explorer)

Prefer not to touch the command line? File Explorer works too:

  • Right-click id_rsa in File Explorer โ†’ Properties
  • Go to the Security tab โ†’ click Advanced
  • Click Disable inheritance โ†’ choose Remove all inherited permissions
  • Click Add โ†’ Select a principal โ†’ type your Windows username โ†’ OK
  • Check Read only โ†’ OK โ†’ Apply

When done, only your user account should appear in the permission list.

Verify the Fix

Before testing SSH, confirm the ACL looks right:

icacls "$env:USERPROFILE\.ssh\id_rsa"

You want to see only your username:

C:\Users\YourName\.ssh\id_rsa YourName:(R)

Successfully processed 1 files; Failed processing 0 files

Now test the connection:

ssh -T git@github.com

Or connect directly to your server:

ssh -i ~/.ssh/id_rsa user@your-server.com

No more warning block. If SSH still fails now, it'll be an authentication error โ€” not a permissions one โ€” which means the key loaded successfully.

Fix for Git Bash / WSL Users

Inside WSL or Git Bash, if the key lives in the WSL filesystem, chmod works fine:

# Inside WSL or Git Bash
chmod 600 ~/.ssh/id_rsa
chmod 700 ~/.ssh

Accessing a Windows-path key from WSL (like /mnt/c/Users/YourName/.ssh/id_rsa)? chmod won't touch the NTFS ACLs. Use icacls from a PowerShell window for those.

Apply to All Keys at Once

Got multiple private keys in your .ssh folder? Fix them all in one shot:

Get-ChildItem "$env:USERPROFILE\.ssh" -File | Where-Object { $_.Name -notmatch '\.pub$|config|known_hosts' } | ForEach-Object {
    $path = $_.FullName
    icacls $path /inheritance:r /remove "NT AUTHORITY\SYSTEM" /remove "BUILTIN\Administrators" /remove "BUILTIN\Users" /grant:r "${env:USERNAME}:R"
    Write-Host "Fixed: $path"
}

Tips

  • Set permissions at key creation time. Running ssh-keygen on Windows sets the ACL correctly by default. Problems almost always come from copying keys between machines.
  • Keep keys inside your user profile. Shared or system directories keep pulling in broad permissions. Stick to C:\Users\YourName\.ssh\.
  • Cross-platform permission translation. If you work across Windows and Linux and need to understand what chmod 600 actually maps to in ACL terms, the Unix Permissions Calculator on ToolCraft makes it visual โ€” runs entirely in the browser, no data sent anywhere.
  • Public keys don't need strict permissions. id_rsa.pub is fine to leave readable. But while you're here, confirm your entire .ssh directory is owned only by your account.

Related Error Notes