OpenSSH, ECDSA, and OS X

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.