First Steps with the Feitian ePass2003 Smart Token in OS X [updated]

2014-11-13

[This post has been updated for Yosemite and now mentions bugs in Apple’s version of ssh-add. Thanks to Fredrik Pettai for letting me know.]

I don’t feel at ease with private keys and other sensitive files floating around on multiple machines and backups. They are of course encrypted, but not accounted for, so it is impossible to “take them back” if ever something goes wrong with a passphrase.

Smart tokens are an attractive solution, because they combine something you know (the PIN) with something you have in the actual, physical sense. Sensitive files are still mobile, but bound to the token.

However, smart tokens are still not mainstream. The software functionality is distributed over several open source projects, which makes it necessary to assemble scattered documentation to see the big picture.

After crawling outdated forum posts on GOOZE and sourcing hardware from an obscure Hungarian web shop (with good service!), I did get token based authentication running with fewer obstacles than I expected. Once you know where to look, it’s actually quite simple.

So here is a mini guide to manage SSH private keys with the Feitian ePass2003 in Mavericks and Yosemite.

Hardware

As I travel a lot, I prefer a compact smart token over a full size smart card and reader combo. The Feitian ePass2003 is a fairly recent product which implements 2048 bit RSA, 256 bit AES and SHA-256 (also DES :-), can generate keys using its own RNG and has 64 KB of storage for keys and certificates.

Software

Three pieces of software are necessary to get the token working: opensc, libusb and libccid. The first two are available from Homebrew. Yosemite ships with a version of libccid that is recent enough to detect the ePass2003. The version shipped with Mavericks is too old, so I used to build the library from source:

cd ccid-1.4.18
./MacOSX/configure
make
sudo make install

Once the three libraries are in place, the token should be recognized

$ opensc-tool -l
# Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             Feitian ePass2003 00 00

Erasing and Formatting the Card

The ePass 2003 contains a proprietary filesystem that needs to be erased first using

pkcs15-init --erase-card

Then create a PKCS#15 compliant file system and set a password

pkcs15-init --create-pkcs15 --profile pkcs15+onepin --label "christian@sigg-iten.ch"

After a successful initialization, the following objects are present on the card

$ pkcs15-tool --dump
Using reader with a card: Feitian ePass2003 00 00
PKCS#15 Card [christian@sigg-iten.ch]:
	Version        : 0
	Serial number  : XXXXXXXXXXXXXXXX
	Manufacturer ID: EnterSafe
	Last update    : 20141106080452Z
	Flags          : EID compliant

PIN [User PIN]
	Object Flags   : [0x3], private, modifiable
	ID             : 01
	Flags          : [0x32], local, initialized, needs-padding
	Length         : min_len:4, max_len:16, stored_len:16
	Pad char       : 0x00
	Reference      : 1 (0x01)
	Type           : ascii-numeric
	Path           : 3f005015

Key Generation and Storage

The ePass2003 can generate keys using its own RNG

pkcs15-init --generate-key rsa/2048 --key-usage sign,decrypt --auth-id 01 --label "christian@sigg-iten.ch"

where the authentication ID refers to the PIN ID. This way the private key never exists outside the token. Generating the key pair on a trusted computer instead is more transparent and makes it possible to backup the private key. Once generated (e.g. using ssh-keygen), the keypair is copied to the token using

pkcs15-init --store-private-key ~/.ssh/id_rsa --auth-id 01 --key-usage sign,decrypt --label "christian@sigg-iten.ch"

Another dump shows the public and private key pair stored on the token

$ pkcs15-tool --dump
Using reader with a card: Feitian ePass2003 00 00
PKCS#15 Card [christian@sigg-iten.ch]:
	Version        : 0
	Serial number  : XXXXXXXXXXXXXXXX
	Manufacturer ID: EnterSafe
	Last update    : 20141106080643Z
	Flags          : EID compliant

PIN [User PIN]
	Object Flags   : [0x3], private, modifiable
	ID             : 01
	Flags          : [0x32], local, initialized, needs-padding
	Length         : min_len:4, max_len:16, stored_len:16
	Pad char       : 0x00
	Reference      : 1 (0x01)
	Type           : ascii-numeric
	Path           : 3f005015

Private RSA Key [christian@sigg-iten.ch]
	Object Flags   : [0x3], private, modifiable
	Usage          : [0x2E], decrypt, sign, signRecover, unwrap
	Access Flags   : [0x1D], sensitive, alwaysSensitive, neverExtract, local
	ModLength      : 2048
	Key ref        : 0 (0x0)
	Native         : yes
	Path           : 3f0050152900
	Auth ID        : 01
	ID             : 460a943632df4d88638d86edb1ca7704a4974e13
	MD:guid        : {faf02afa-6da5-f738-9c36-629582716306}
	  :cmap flags  : 0x0
	  :sign        : 0
	  :key-exchange: 0

Public RSA Key [christian@sigg-iten.ch]
	Object Flags   : [0x2], modifiable
	Usage          : [0xD1], encrypt, wrap, verify, verifyRecover
	Access Flags   : [0x0]
	ModLength      : 2048
	Key ref        : 0 (0x0)
	Native         : no
	Path           : 3f0050153000
	ID             : 460a943632df4d88638d86edb1ca7704a4974e13
	DirectValue    : <absent>

Token Based SSH Authentication

OS X ships with an old version of OpenSSH. What is worse, ssh-add is buggy when used with a PKCS#11 provider. I use the Homebrew package openssh instead.

For a token based login, extract the public key in SSH compatible format

$ pkcs15-tool --read-ssh-key 460a943632df4d88638d86edb1ca7704a4974e13
[Using reader with a card: Feitian ePass2003 00 00
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6yaaL1F6YE4ISemTt1a2qcWK9kOt4...

and append it to .ssh/authorized_keys on the server. Then specify opensc-pkcs11.so as the PKCS#11 provider to authenticate using the token

$ ssh -I /usr/local/lib/opensc-pkcs11.so user@host
Enter PIN for 'christian@sigg-iten.ch (User PI': 
Last login: Thu Nov  6 08:51:12 2014 from x.x.x.x
[user@host ~]$

A default provider is configured by adding

PKCS11Provider opensc-pkcs11.so

to .ssh/config. Then it is enough to just type

$ ssh user@host

Unfortunately, this configuration directive seems to conflict with an ssh-agent based workflow. To use the agent, don’t specify a default provider and use

$ eval `ssh-agent -s`
Agent pid 11996
$ ssh-add -s /usr/local/lib/opensc-pkcs11.so 
Enter passphrase for PKCS#11: 
Card added: /usr/local/lib/opensc-pkcs11.so
$ ssh user@host

Troubleshooting

This OpenSC wiki page describes how to enable debug logs for targeted bug report searches.