How to Update OpenSSH on Mac OS X

Just for kicks, I wanted to try using an ECDSA key for ssh authentication. Unfortunately, the OpenSSH bundled with Mountain Lion (10.8) does not support ECDSA keys (nor can one even be generated with ssh-keygen.) The man pages for ssh-keygen and ssh-agent say they support ECDSA, but this is due to a naive man page generation assuming that since the OpenSSL library supports it, OpenSSH will too. Also, a PCI compliant OpenSSH isn’t bundled with OS X Lion (10.7) or older so this will also be useful for those users as well. Thankfully, Homebrew already has a recipe for installing an up-to-date OpenSSH so most of the work of upgrading is already done.

If you don’t already have Homebrew installed, follow its installation instructions first.

1. First we’ll need to add the system duplicates repository to Homebrew.

    $ brew tap homebrew/dupes

2. Compile and install OpenSSH. I want to use a newer OpenSSL and all its optimizations, which Homebrew will happily provide via an option. Also, to make ssh-agent launchd and keychain compatible, there’s a nice undocumented option to apply the necessary patch before compiling too.

    $ brew install openssh --with-brewed-openssl --with-keychain-support

3. Like the caveat notes when brew finishes, you need to update the launchd plist for ssh-agent to use the new Homebrew binary. By replacing /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

4. The SSH_AUTH_SOCK env var needs to be updated for any open or new terminal sessions. It’s best to logout/login or restart because we cannot modify the variables in the user session scope that all new processes inherit from. However, if that’s not an option, can do this instead.

    $ export SSH_AUTH_SOCK=$(launchctl getenv SSH_AUTH_SOCK)

5. Generate an ECDSA key

    $ ssh-keygen -t ecdsa -b 521

Once the pub key from your new ECDSA key pair is added to .ssh/authorized_keys on your server(s), should be good to go (assuming OpenSSH on your server also supports ECDSA keys.)

Here are openssl speed runs showing considerable improvements in the newer OpenSSL on a Late-2012 rMBP with a 2.9 Ghz i7 (Ivy Bridge):

OpenSSL 0.9.8x 10 May 2012
built on: Apr 27 2013
options:bn(64,64) md2(int) rc4(ptr,char) des(idx,cisc,16,int) aes(partial) blowfish(ptr2)
compiler: -arch x86_64 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -O3 -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DMD32_REG_T=int -DOPENSSL_NO_IDEA -DOPENSSL_PIC -DOPENSSL_THREADS -DZLIB -mmacosx-version-min=10.6
available timing options: TIMEB USE_TOD HZ=100 [sysconf value]
timing function used: getrusage
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
md5              38143.69k   119545.89k   277327.98k   421224.64k   490718.66k
hmac(md5)        44242.06k   134438.19k   293790.30k   422019.19k   494423.98k
aes-128-cbc     170039.77k   180261.34k   180652.73k   183675.79k   184316.33k
aes-256-cbc     134118.17k   139409.38k   137316.74k   141434.10k   141356.07k
sha256           28026.63k    68129.47k   133706.87k   174197.11k   190001.40k
sha512           17881.04k    70711.63k   145642.06k   237958.34k   291830.79k
                  sign    verify    sign/s verify/s
rsa 4096 bits 0.029424s 0.000394s     34.0   2537.2
                              sign    verify    sign/s verify/s
 224 bit ecdsa (nistp224)   0.0002s   0.0008s   5787.7   1251.5
 256 bit ecdsa (nistp256)   0.0002s   0.0010s   4822.8   1004.9
 521 bit ecdsa (nistp521)   0.0005s   0.0026s   1946.9    380.4
OpenSSL 1.0.1e 11 Feb 2013
built on: Tue Jul 30 01:06:27 PDT 2013
options:bn(64,64) rc4(ptr,char) des(idx,cisc,16,int) aes(partial) idea(int) blowfish(idx)
compiler: cc -fPIC -fno-common -DOPENSSL_PIC -DZLIB_SHARED -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -arch x86_64 -O3 -DL_ENDIAN -Wall -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
md5              73628.17k   215121.49k   448613.97k   613620.39k   693551.10k
hmac(md5)        50714.32k   161230.70k   382580.65k   575982.93k   687741.69k
aes-128-cbc     641533.32k   687496.81k   701280.68k   702194.35k   699692.69k
aes-256-cbc     469374.78k   495378.58k   499366.40k   501357.57k   501394.24k
sha256           54268.49k   118861.46k   208064.34k   255732.74k   275634.77k
sha512           41191.81k   161831.10k   256644.61k   368996.35k   426789.55k
                  sign    verify    sign/s verify/s
rsa 4096 bits 0.008642s 0.000136s    115.7   7374.0
                              sign    verify    sign/s verify/s
 224 bit ecdsa (nistp224)   0.0001s   0.0001s  14421.9   7391.3
 256 bit ecdsa (nistp256)   0.0001s   0.0003s   8765.6   3839.2
 521 bit ecdsa (nistp521)   0.0004s   0.0008s   2636.1   1205.2

  • David

    Just tried all of the following with Mavericks, and afterwards I got from the ssh -v command:

    OpenSSH_6.2p2, OSSLShim 0.9.8r 8 Dec 2011

    Doesn’t seem like it worked, have the instructions changed for Mavericks? I even tried changing the path in /System/Library/LaunchDaemons/ssh.plist as well, but that had no effect.

    • David

      Ah, after adding this to ~/.bash_profile all worked correctly:

      # Ensure user-installed binaries take precedence
      export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

  • Johan

    This actually broke for me as of 10.9.1 and I (so far) haven’t been able to figure out why. the launchd plist exits with “exit 1″. Invoking /usr/local/bin/ssh-agent manually works just fine. I tried getting more debug out of launchd, but this is the most verbose I’ve manage to scrape together:

    25/12/2013 8:33:06.331 pm com.apple.launchd.peruser.501[175]: Background: assertion failed: 13B42: launchd + 63764 [C35AEAF6-FCF6-3C64-9FC8-38829064F8A8]: 0x0

    If I remove the -l flag, launchd starts it but behaves differently: it launches one every 10 seconds.

    The reason for me replying here is since I haven’t found any other place where a homebrew openssh dialogue seems to be brewing (ha ha). Anyone else seeing this?

    • Kip M

      This broke for me as of 10.9.1 too. Have you found a way to fix it yet?

      • Johan

        No, but if I find one I’ll post to this thread. (Sorry blog owner for using this as a forum thread)

        • gidj

          There seems to be the same issue being tracked at Homebrew’s Github:

          https://github.com/Homebrew/homebrew-dupes/issues/242

          The final remark indicates that the reason for the problem is that Homebrew’s version doesn’t build with a feature needed by launchd. There is not a working solution yet, though.

  • Pingback: Joseph Barker » HPN-SSH on OSX with Homebrew()

  • Martin

    Did not work for me either. So please remove this page for others to avoid wasting their time.

    • Matijs

      Nice one :| this is by far the best piece I’ve come across in my search to replace system OpenSSH with one that supports ECDSA. Nice work John!

  • pixelBender67

    now I have to submit my password on every push or pull I do to github. How do I get rid of this behavior?
    or undo what this article does?

  • mochtu

    I had to reboot after installing on OSX 10.10.1, unloading/loading was not enough. Now it’s working fine!

  • Russell

    Please don’t use ECDSA for your keys. It uses NIST curves which have problems and NIST related schemes should be considered untrusted.

    A much better alternative is Ed25519 with SHA512.