SSH 8.2 introduced support for using any U2F key in place of a private key file. Using it on macOS with full support for
ssh-agent is a bit more complex.
Generating the keys
You must choose between
ed25519-sk(Options 1 or 3) first. If it does not work due to device incompatibilities, fall back on
ecdsa-sk(Options 2 or 4)
You must choose if you want to store the key handle as a resident key on the device. If you want to, use options 1 or 2. If not, use options 3 or 4.
A U2F attestation requires a key handle to be sent to the device. When generating the key,
ssh-keygenwill create private and public key files that look similar to normal ssh key. The private key file is actually a key handle that cannot be used without the hardware token, however, the hardware token can also not be used without the key handle.
A resident key solves this problem by storing the key handle on the device. However, your key may or may not support it and only a limited number of resident keys may be stored on a device. Additionally, it may reduce the security of your ssh key as they could use it if they steal the hardware device. For this reason, a good pin is important.
It is your choice whether to use a resident key. If you do, you can load it directly to the ssh-agent using
ssh-add -K, or write the key handle and public key to disk using
ssh-keygen -t ed25519-sk -O resident # 1 ssh-keygen -t ecdsa-sk -O resident # 2 ssh-keygen -t ed25519-sk # 3 ssh-keygen -t ecdsa-sk # 4
SSH v8.2 is required to use a security key. Install it with brew.
brew install openssh
You can specifiy the path to the private key handle in your ssh config. Otherwise, you can configure the ssh-agent.
ssh-agent on macOS
To be used with a security key, the ssh-agent must be on v8.2, which the system default is not.
First, disable the macOS default ssh-agent for your user.
launchctl disable user/$UID/com.openssh.ssh-agent
Next, add a new launchd service for your ssh-agent. Add the following file to
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.zerowidth.launched.ssh_agent</string> <key>ProgramArguments</key> <array> <string>sh</string> <string>-c</string> <string>/usr/local/bin/ssh-agent -D -a ~/.ssh/agent</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
And load it with
launchctl load -w ~/Library/LaunchAgents/com.zerowidth.launched.ssh_agent.plist.
This plist was created using the launchd plist generator over at zerowidth. It runs the command
/usr/local/bin/ssh-agent -D -a ~/.ssh/agent.
-D prevents ssh-agent from forking, and
-a ~/.ssh/agent directs the agent to create a socket file at that location that is referenced in