Temporary work-around to allow using PKCS#8-hardened SSH-keys with Mavericks (10.9)

Introduction

Like many people, I read Martin Kleppmann’s article on Improving the security of your SSH private key files and Aaron Toponce’s Strengthen Your Private Encrypted SSH Keys; and then duly hardened my own keys. Everything was good… until Apple released Mavericks and I could no longer import my keys :-O

Fortunately, there’s a simple fix: just downgrade your keys again:

openssl rsa -in id_hardened_rsa -aes256 -out id_rsa

Unfortunately, that’s just undone all the good work of hardening the keys! :(

There is another solution: install a version of OpenSSH that supports PKCS#8 encoded keys. But what if Apple fixes the problem later, or there are other Apple-specific tweaks? Surely there’s a better way…?

The better way

What if we could just update the bits that load the keys (ssh-add and perhaps ssh-agent too) without upsetting the rest of the installation? That’s what I set out (and managed) to do.

Dependencies

There are a few things we need to download first and this does all assume you have XCode installed. At the time of writing, I’ve used OpenSSH v6.4p1, OpenSSL v1.0.0e, autoconf v2.69 and the patches from the MacPort of OpenSSH:

Build autoconf

Just build autoconf, there’s no need to install it:

tar xzf autoconf-2.69.tar.gz
cd autoconf-2.69
./configure
make

Build OpenSSL

Again, just build OpenSSL, no need to install it. If you need the i386 version, just configure for darwin-i386-cc instead:

tar -xvzf openssl-1.0.0e.tar.gz
cd openssl-1.0.0e
./Configure darwin64-x86_64-cc -shared
make

Extract OpenSSH

Extract and patch:

tar -xvzf openssh-6.4p1.tar.gz
cd openssh-6.4p1
patch -p1 < ../0002-Apple-keychain-integration-other-changes.patch
patch -p1 < ../openssh-6.3p1-gsskex-all-20130920.patch

I found that the 0002-Apple-keychain-integration-other-changes.patch patch needed a bit of massaging; the flags for adding all keys that have passwords stored in the keychain (-A) and to store new passwords in the keychain (-K) were mapped to -m and -M respectively, and that getopt() was patched to accept an additional ‘K’. I suspect this’ll be addressed at some point, or there might be a better place to download the patchfile, but, in the meantime, I added my own patch:

--- a/ssh-add.c 2013-12-06 01:08:05.000000000 +0000
+++ b/ssh-add.c 2013-12-06 01:09:03.000000000 +0000
@@ -457,7 +457,7 @@
                    "Could not open a connection to your authentication agent.\n");
                exit(2);
        }
-       while ((ch = getopt(argc, argv, "kKlLcdDxXe:s:t:")) != -1) {
+       while ((ch = getopt(argc, argv, "AkKlLcdDxXe:s:t:")) != -1) {
                switch (ch) {
                case 'k':
                        key_only = 1;
@@ -496,11 +496,11 @@
                                goto done;
                        }
                        break;
-               case 'm':
+               case 'A':
                        if (add_from_keychain(ac) == -1)
                                ret = 1;
                        goto done;
-               case 'M':
+               case 'K':
                        keychain = 1;
                        break;
                default:

PAM

This is my one concession to not altering the system. Apple placed the PAM header files in /usr/include/security, so it’s just easiest to create a symlink:

sudo ln -s security /usr/include/pam

Rebuild OpenSSH’s configure script

This is purely because I didn’t want to install autoconf system-wide. If you have, just run autoconf and all is done. Otherwise, things need “fixing-up” to reference the not-yet-installed build directory:

AUTOM4TE_CFG=../autoconf-2.69/lib/autom4te.cfg PERLLIB=../autoconf-2.69/lib/ AUTOM4TE=../autoconf-2.69/bin/autom4te ../autoconf-2.69/bin/autoconf --prepend-include=../autoconf-2.69/lib/

Configure OpenSSH

These options were mostly taken from the MacPort; with two tweaks to point the configure script at the not-yet-installed OpenSSL directory:

DYLD_LIBRARY_PATH=../openssl-1.0.0e ./configure --with-ssl-dir=../openssl-1.0.0e --with-keychain=apple --sysconfdir=/etc/ssh --with-privsep-path=/var/empty --with-md5-passwords --with-pid-dir=/var/run --without-tcp-wrappers --with-pam --mandir=/ CFLAGS=-D__APPLE_LAUNCHD__

Building OpenSSH

First, add two new targets to the Makefile; these are for the semi-static (linked against OpenSSL v1.0.0e) versions of ssh-add and ssh-agent:

printf 'ssh-add-static$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(KEYCHAINOBJS)\n' >> Makefile
printf '\t$(LD) -o $@ ssh-add.o $(KEYCHAINOBJS) ../openssl-1.0.0e/libcrypto.a ../openssl-1.0.0e/libssl.a $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(filter-out -lcrypto,$(LIBS))\n' >> Makefile
printf 'ssh-agent-static$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS)\n' >> Makefile
printf '\t$(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(KEYCHAINOBJS) ../openssl-1.0.0e/libcrypto.a ../openssl-1.0.0e/libssl.a $(LDFLAGS) $(KEYCHAIN_LDFLAGS) -lssh -lopenbsd-compat $(filter-out -lcrypto,$(LIBS))\n' >> Makefile

And build!

make
make ssh-add-static
make ssh-agent-static

Usage

At this point, I was successfully able to load my hardened keys into the existing agent:

./ssh-add-static ~/.ssh/id_hardened_rsa

If I wanted to make this semi-permanent, then I’d install the two updates (backing up the originals first) and logout so the updated agent took effect:

sudo mv /usr/bin/ssh-add /usr/bin/ssh-add.orig
sudo install -o root -g wheel -m 0755 ssh-add-static /usr/bin/ssh-add
sudo mv /usr/bin/ssh-agent /usr/bin/ssh-agent.orig
sudo install -o root -g wheel -m 0755 ssh-agent-static /usr/bin/ssh-agent