How to generate an SSH key on Windows

Windows 10 and 11 ship OpenSSH, so PuTTYgen is no longer needed. Create an Ed25519 key in PowerShell, enable ssh-agent and use the key with servers and Git.

updated

Quick answer. Open PowerShell, run the first command and press Enter to keep the default location. The key pair is saved in C:\Users\you\.ssh; the second command copies the public key to the clipboard.

ssh-keygen -t ed25519 -C "[email protected]"
Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub" | Set-Clipboard

Before you start

An SSH key is a pair of files. The private key stays on your PC; the public key goes to GitHub, GitLab or a server, which then lets in whoever holds the private key. It replaces passwords and tokens for ssh and git, and lets a server turn password logins off.

Windows has carried Microsoft's port of OpenSSH since Windows 10 version 1809. It is the same ssh, ssh-keygen and ssh-add that Linux and macOS use, so keys and config files are interchangeable. You do not need PuTTY, Git Bash or WSL to create a key, though all three can use it.

Use the Ed25519 key type; it is the recommended default and every maintained service accepts it. The reasons, and when RSA still matters, are in Ed25519 vs RSA.

How to generate an SSH key on Windows

  1. Check that the OpenSSH client is installed

    Windows 10 (version 1809 and later) and Windows 11 include the OpenSSH client. Open PowerShell and run:

    ssh -V

    If the command is not found, install it from Settings, System, Optional features, OpenSSH Client, or from an Administrator PowerShell with Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0.

  2. Generate the key pair

    In a normal PowerShell window, run ssh-keygen. Press Enter to accept the default location and type a passphrase twice.

    ssh-keygen -t ed25519 -C "[email protected]"

    The files land in C:\Users\you\.ssh\id_ed25519 and id_ed25519.pub. Always pass -t ed25519: older Windows builds of OpenSSH create an RSA key when no type is given. Use ssh-keygen -t rsa -b 4096 only for systems that do not accept Ed25519.

  3. Enable and start ssh-agent

    The agent service ships disabled. In PowerShell run as Administrator, set it to start automatically and start it now:

    Get-Service ssh-agent | Set-Service -StartupType Automatic
    Start-Service ssh-agent
  4. Add the key to the agent

    Back in a normal PowerShell window, add the key and enter its passphrase. The Windows agent keeps it, protected by your Windows account, across reboots.

    ssh-add "$env:USERPROFILE\.ssh\id_ed25519"
  5. Copy the public key

    Put the public key on the clipboard to paste into GitHub, GitLab or a hosting panel:

    Get-Content "$env:USERPROFILE\.ssh\id_ed25519.pub" | Set-Clipboard
  6. Install the key on a Linux server

    Windows has no ssh-copy-id. This one-liner does the same job: it logs in with your password once and appends the key to authorized_keys with the right permissions.

    type "$env:USERPROFILE\.ssh\id_ed25519.pub" | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

What ssh-keygen prints on Windows

PS C:\Users\you> ssh-keygen -t ed25519 -C "[email protected]"
Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\you/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\you/.ssh/id_ed25519
Your public key has been saved in C:\Users\you/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:I2McFhOvOOgBGIf3aLmk0z7TTKxZoLZ32hK6Bxbk7Tw [email protected]
The key's randomart image is:
+--[ED25519 256]--+
|...   +.         |
|o+.    +         |
|=..+  o .        |
| +*o.+ o         |
| **+o * S        |
|+=oE+o o .       |
|oo=Bo            |
| o*++.           |
| .+++.           |
+----[SHA256]-----+

The mixed slashes in the path are normal for the Windows port. Nothing appears while you type the passphrase. If the file already exists, ssh-keygen asks Overwrite (y/n)?: answer n unless you mean to replace a key that servers already trust. The fingerprint at the end identifies the key; see SSH key fingerprints.

Where Windows stores SSH keys

FileWhat it is
C:\Users\<you>\.ssh\id_ed25519Private key. Never share or upload it.
C:\Users\<you>\.ssh\id_ed25519.pubPublic key, one line starting with ssh-ed25519.
C:\Users\<you>\.ssh\configOptional per-host settings.
C:\Users\<you>\.ssh\known_hostsHost keys of servers you have connected to.

$env:USERPROFILE in PowerShell and %USERPROFILE% in Command Prompt both point at C:\Users\<you>, and ~ works in ssh's own config. Explorer shows the folder with explorer "$env:USERPROFILE\.ssh".

The ssh-agent service

On Windows, ssh-agent is a system service, not a program you start per window. Once enabled (step 3) it runs at boot, and keys added with ssh-add are stored for your account and survive restarts. That differs from Linux and macOS, where the agent forgets keys at logout.

Get-Service ssh-agent          # Status should be Running, StartType Automatic
ssh-add -l                     # list loaded keys
ssh-add -d "$env:USERPROFILE\.ssh\id_ed25519"   # remove one key
ssh-add -D                     # remove all keys

Because the agent remembers keys indefinitely, anyone signed in to your Windows account can use them. Lock the PC when you leave it, and see SSH key passphrases for agent lifetimes.

Fixing "Bad permissions" on the private key

OpenSSH refuses a private key other accounts can read (WARNING: UNPROTECTED PRIVATE KEY FILE!). Downloaded or copied files may inherit broad permissions. Grant access to your account only:

icacls "$env:USERPROFILE\.ssh\id_ed25519" /inheritance:r /grant:r "$($env:USERNAME):F"

/inheritance:r removes the permissions copied from the parent folder and /grant:r leaves full control to you alone. Running icacls on the file without options lists who has access. The same command fixes a config file that ssh rejects.

You can also generate a key in your browser with the sshkeygen.dev generator; its downloads need this fix. Move both files into .ssh first:

New-Item -ItemType Directory -Force "$env:USERPROFILE\.ssh" | Out-Null
Move-Item "$env:USERPROFILE\Downloads\id_ed25519" "$env:USERPROFILE\.ssh\"
Move-Item "$env:USERPROFILE\Downloads\id_ed25519.pub" "$env:USERPROFILE\.ssh\"

Making Git use the Windows agent

Git for Windows bundles its own OpenSSH, which ignores the Windows agent, so Git keeps asking for the passphrase. Point Git at the system client once:

git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"

Then test with ssh -T [email protected]. A working key answers Hi your-username! You've successfully authenticated, but GitHub does not provide shell access. Adding the key to your account is covered in SSH key for GitHub.

Inside Git Bash itself, ssh is Git's copy. It reads the same ~/.ssh folder but needs its own agent: run eval "$(ssh-agent -s)" and ssh-add in that window.

An ssh config file on Windows

The client reads %USERPROFILE%\.ssh\config, with the usual syntax:

Host web
  HostName 203.0.113.10
  User deploy
  Port 22
  IdentityFile ~/.ssh/id_ed25519

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

Now ssh web is enough. The tilde works on Windows too and means your profile folder. The second block lets a second key, made with ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\id_ed25519_work", serve a second GitHub account: clone with git@github-work:company/repo.git. Save the file without an extension; Notepad adds .txt unless you choose "All files".

PuTTY, WSL and other tools

  • PuTTY and WinSCP use their own .ppk format. Open PuTTYgen, choose Conversions, Import key, select id_ed25519, then Save private key. The OpenSSH key itself stays as it is.
  • From .ppk to OpenSSH: load the .ppk in PuTTYgen and choose Conversions, Export OpenSSH key. The public key line shown in PuTTYgen's window is already in the format servers and GitHub expect.
  • WSL distributions have their own Linux home folder with a separate ~/.ssh. Either generate a key inside WSL, following the Linux guide, or copy the Windows key in and fix its mode, since files under /mnt/c appear world-readable: cp /mnt/c/Users/you/.ssh/id_ed25519* ~/.ssh/ && chmod 600 ~/.ssh/id_ed25519.
  • Windows Terminal and VS Code use the system OpenSSH, so the agent and config above apply to them directly.

Keys for a Windows OpenSSH server

If the machine you connect to runs Windows with OpenSSH Server, a standard user's keys go in C:\Users\<user>\.ssh\authorized_keys. For members of the Administrators group, sshd reads C:\ProgramData\ssh\administrators_authorized_keys instead, and only if Administrators and SYSTEM alone can access it:

icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"

Putting an administrator's key in their own authorized_keys is the most common reason key login fails on a Windows server. For Linux servers, see adding a key to a server.

Troubleshooting

"Error connecting to agent: No such file or directory"

The ssh-agent service is not running. Repeat step 3 as Administrator.

"Permission denied (publickey)"

Run ssh -v user@host to see which keys are tried. On a Windows server, administrators' keys go in C:\ProgramData\ssh\administrators_authorized_keys. More in adding a key to a server.

"WARNING: UNPROTECTED PRIVATE KEY FILE!"

Other accounts can read the key file. Run the icacls command from the permissions section, then try again.

"Bad owner or permissions on C:\\Users\\you/.ssh/config"

The config file is writable by other accounts, often because it was created by an elevated process or copied from elsewhere. Apply the same icacls command to $env:USERPROFILE\.ssh\config.

"Could not open a connection to your authentication agent"

You are in Git Bash or WSL, where the Windows service is not visible. Start an agent in that shell with eval "$(ssh-agent -s)", or use PowerShell.

"Host key verification failed"

The server's host key differs from the one in known_hosts. After confirming the new fingerprint with the server's owner, remove the old entry with ssh-keygen -R host and connect again.

"no matching host key type found. Their offer: ssh-rsa"

The server only supports the SHA-1 RSA signature that current OpenSSH disables. Update the server, or for that host only add HostKeyAlgorithms +ssh-rsa and PubkeyAcceptedAlgorithms +ssh-rsa to its block in the config file.

"ssh-keygen is not recognized"

The OpenSSH client feature is not installed. See step 1.

FAQ

Do I still need PuTTY or PuTTYgen to create an SSH key on Windows?

No. Windows 10 version 1809 and later and Windows 11 include OpenSSH, so ssh-keygen in PowerShell or Command Prompt is enough. You only need PuTTYgen to convert a key to .ppk for PuTTY or WinSCP.

Where is my SSH key on Windows?

In C:\Users\your-name\.ssh: id_ed25519 is the private key and id_ed25519.pub the public key. Open the folder with explorer "$env:USERPROFILE\.ssh".

Does ssh-agent on Windows remember keys after a reboot?

Yes. Unlike the agent on Linux and macOS, the Windows ssh-agent service stores added keys, encrypted for your Windows account, and offers them again after a restart. Remove a key with ssh-add -d or all of them with ssh-add -D.

Can I use the same key in PowerShell, Git Bash and WSL?

PowerShell, Command Prompt, Windows Terminal and VS Code share the system OpenSSH and its agent. Git Bash uses its own ssh unless you set core.sshCommand. WSL has a separate Linux home; copy the key there and run chmod 600, or create a separate key inside WSL.

How do I copy the public key in Command Prompt?

Run type %USERPROFILE%\.ssh\id_ed25519.pub | clip. In PowerShell, use Get-Content piped to Set-Clipboard.

Can I run ssh-keygen in Command Prompt instead of PowerShell?

Yes, the command is the same. Only the helper commands differ, such as type and clip instead of Get-Content and Set-Clipboard, and %USERPROFILE% instead of $env:USERPROFILE.

Why does ssh-add say "Error connecting to agent: No such file or directory"?

The ssh-agent service is stopped or disabled, which is the default on a fresh Windows install. In an Administrator PowerShell, set its startup type to Automatic and start it, as in step 3.