OpenSSH, ECDSA, and OS X
17.02.2014 - Louis Kowolowski - ~4 Minutes
I’ve been wanting to play with ECDSA ssh keys for a while (If memory serves, it became available in early 2011).
I hoped that 10.7 (Lion) would support it (the man pages confused things because they talk about generating and using ECDSA keys, but the utilities don’t actually do it). Then I hoped that 10.8 (Mountain Lion) would support. I finally gave up and installed my own version of OpenSSH through Homebrew . After a bit of googling, and some trial and error, here are the steps I arrived at successfully.
Homebrew dupes
We need to add the dupes tap to Homebrew. This is essentially an extra repo, but all the recipes in it, duplicate things that are installed by default in the system.
brew tap homebrew/dupes
Compile/install OpenSSH
We also want to bring in a new OpenSSL with extra optimizations, which is easy with Homebrew. As a last bit to this step, we will ensure that the new ssh-agent is launchd and keychain compatible.
brew install openssh --with-brewed-openssl --with-keychain-support
Tweaking launchd
We need to fix the launchd plist if we want to use the new agent (which we do, because how else would you manage all your ssh keys?). The overview is to simply replace ‘/usr/bin/ssh-agent’ with ‘/usr/local/bin/ssh-agent’.
launchctl stop org.openbsd.ssh-agent
launchctl unload -w /System/Library/LaunchAgents/org.openbsd.ssh-agent.plist
sudo vi /System/Library/LaunchAgents/org.openbsd.ssh-agent.plist
launchctl load -w -S Aqua /System/Library/LaunchAgents/org.openbsd.ssh-agent.plist
Updating the environment
The SSH_AUTH_SOCK environment variable needs to be updated for new or existing terminal sessions. The slacker way is to logout or restart. Just export the variable, or setenv if you (t)csh. (ba,z)sh:
export SSH_AUTH_SOCK=$(launchctl getenv SSH_AUTH_SOCK)
(t)csh:
setenv SSH_AUTH_SOCK "`launchctl getenv SSH_AUTH_SOCK"
Generating an ECDSA key
Now we can finally generate our ECDSA key and add your new key to your ssh-agent(1)
[louisk@iPwn louisk ]$ ssh-keygen -t ecdsa
Generating public/private ecdsa key pair.
Enter file in which to save the key (/Users/louisk/.ssh/id_ecdsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/louisk/.ssh/id_ecdsa.
Your public key has been saved in /Users/louisk/.ssh/id_ecdsa.pub.
The key fingerprint is:
b5:06:56:e9:0b:c0:ce:21:6c:2b:f3:88:19:df:bc:61 louisk@iPwn
The key's randomart image is:
+---[ECDSA 256]---+
| . . .. |
| + + .. |
| . = oo.. |
|. o . o..o.. |
| = B S.o. |
|o o E .. |
| . o |
| . |
| |
+-----------------+
[louisk@iPwn louisk ]$
[louisk@iPwn louisk ]$ ssh-add .ssh/id_ecdsa
Enter passphrase for .ssh/id_ecdsa:
Identity added: .ssh/id_ecdsa (.ssh/id_ecdsa)
[louisk@iPwn louisk ]$ ssh-add -l
521 b3:0c:26:1a:58:23:87:5c:fc:b0:db:51:9a:92:55:ae .ssh/id_ecdsa (ECDSA)
[louisk@iPwn louisk ]$
Testing the key
Don’t forget to copy your new key (by default: id_ecdsa.pub) to your ~/.ssh/authorized_keys on your remote hosts. You should now be able to login to a host using an ECDSA key exchange. Evidence of success can be seen by looking at the verbose output from ssh (logging in to a Juniper switch).
[louisk@mpro louisk 177 ]$ ssh -v 198.18.0.2
OpenSSH_6.4, OpenSSL 1.0.1f 6 Jan 2014
debug1: Reading configuration data /Users/louisk/.ssh/config
debug1: /Users/louisk/.ssh/config line 59: Applying options for *
debug1: Reading configuration data /usr/local/etc/ssh/ssh_config
debug1: Connecting to 198.18.0.2 [198.18.0.2] port 22.
debug1: Connection established.
debug1: identity file /Users/louisk/.ssh/id_ecdsa type 3
debug1: identity file /Users/louisk/.ssh/id_ecdsa-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_6.4
debug1: Remote protocol version 2.0, remote software version OpenSSH_6.1
debug1: match: OpenSSH_6.1 pat OpenSSH*
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: server->client aes128-ctr hmac-md5 none
debug1: kex: client->server aes128-ctr hmac-md5 none
debug1: sending SSH2_MSG_KEX_ECDH_INIT
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ECDSA 2e:5a:65:f0:bc:42:d2:22:e6:7d:71:65:0c:4f:b0:06
debug1: Host '198.18.0.2' is known and matches the ECDSA host key.
debug1: Found key in /Users/louisk/.ssh/known_hosts:196
debug1: ssh_ecdsa_verify: signature correct
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: Roaming not allowed by server
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Offering ECDSA public key: /Users/louisk/.ssh/id_ecdsa
debug1: Server accepts key: pkalg ecdsa-sha2-nistp521 blen 172
debug1: Authentication succeeded (publickey).
Authenticated to 198.18.0.2 ([198.18.0.2]:22).
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: Requesting authentication agent forwarding.
--- JUNOS 13.2X50-D15.3 built 2013-09-22 23:13:21 UTC
{master:0}
louisk@switch0.cmhome> exit
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: client_input_channel_req: channel 0 rtype eow@openssh.com reply 0
debug1: channel 0: free: client-session, nchannels 1
Connection to 198.18.0.2 closed.
Transferred: sent 3416, received 2240 bytes, in 7.2 seconds
Bytes per second: sent 473.1, received 310.2
debug1: Exit status 0
[louisk@mpro louisk 178 ]$
Its probably worth noting that if you don’t put “/usr/local/bin” ahead of “/usr/bin” in your shell “$PATH”, things won’t work the way you want. All you have to do is add “/usr/local/bin” first, then everything works fine.
Update for Yosemite
UPDATE: This works fine with OS X Yosemite 10.10
Update for El Capitan
UPDATE: In order to make the plist changes, you will need to disable rootless. Please see this article on rootless and boot-args .
ssh-agent (from homebrew) is not working currently. This may (or may not) get fixed for the release.