MacOS PIV Setup Guide

A somewhat hidden feature of MacOS is that it has excellent builtin support for PIV (smartcard) authentication. In this guide, we'll go through how to set that up.

In this example we'll be using yubikey 5's as our smartcards. This may require you to alter some commands used in this guide.

Prerequisites

If you are using yubico's piv tool you should install it from yubico's site for the signed binaries rather than installing from homebrew.

You should also have a plan for how you want to use your key. Do you want it just for SSH? Do you want to login to your laptop with the key? Are you happy to install and use tools from brew over the default MacOS tools?

Setup the Smartcard

When you first setup the smartcard you need to configure the management key, the personal identification number (PIN) and the PIN unblock key (PUK). To do this we will reset the smartcard, clearing all existing keys from it's storage.

NOTE: This is a destructive action, if you have keys already on the smartcard they can not be recovered.

First reset the PIV applet. To do this you need the PIN and PUK to both be blocked.

# Repeat until the PIN is blocked
yubico-piv-tool -a verify-puk --puk aaaaaa
# Repeat until the PUK is blocked
yubico-piv-tool -a unblock-pin -P aaaaaa -N aaaaaa

If done correctly you will see:

The puk code is blocked, you will have to reinitialize the application.

The device can now be reset with:

yubico-piv-tool -a reset

Management Key

The management key is responsible for authorising key generation, deletion and importing new certificates. This prevents a holder from changing or modifying the keys of the smartcard even if they know the PIN.

You can now configure the management key. The management key should consist of only 0-9a-f and be 32 characters in length. An example is 1f45846d1c47debd2274f9f48a96f196.

NOTE: Optionally append --touch-policy=always if you want to enforce the key must be touched for management operations.

# yubico-piv-tool -a set-mgm-key --new-key-algo=AES128
Enter new management key:

PIN

The PIN is used by the person (holder) of the smartcard to authenticate to the device proving their identity. The PIN is used to authorise signatures and decryption of data. Importantly, knowledge of the PIN allows changing the PIN as well.

After the card is reset, the default PIN is inuse.

⚠️ WARNING: Some clients (such as MacOS) will only accept numeric PINs. Yubikeys will work with alphanumeric PINs but setting one may break applications.

yubico-piv-tool -a change-pin -P 123456
Enter new pin:

PUK

The PUK has a single function - allow reseting the PIN in the case that it is lost or forgotten. By default, an incorrect PIN can be supplied up to 3 times before the PIN is blocked. If both the PUK and PIN are forgotten then the device must be reset. This makes it important that you keep the PUK secure in case this occurs.

After the card is reset, the default PUK is inuse.

yubico-piv-tool -a change-puk -P 12345678
Enter new puk:

CHUID

The Card Holder Unique IDentifier is used by MacOS and other applications to identify the user. If not set, a number of PIV elements will not work. The CHUID can be generated with:

yubico-piv-tool -k -a set-chuid

Configure Smartcard Slots

Smartcards have many credential slots, but there are only 4 slots that we will concern ourselves with in this guide. Yubico has complete documentation their purposes.

  • 9A - PIV Authentication - Authenticate the user for login to systems
  • 9C - Digital Signature - Sign files or executables for the user
  • 9D - Key Management - decrypt files for the user
  • 9E - Card Authentication - Authenticate the smartcard, generally for physical system access

IMPORTANT: Do not alter or modify the content of slot F9 as modifications can NOT be undone even with a complete device reset.

For the purposes of MacOS we only require slots 9A and 9D to be configured.

Generate a key in slot 9A. We set the pin policy and touch policy to once and cached respectively so that multiple authentications in quick succession do not require repetitive user input.

NOTE: Remember to touch your key when it blinks as required!

IMPORTANT: If you plan to use the default MacOS ssh agent, you MUST set the algorithm to RSA2048 for slot 9A.

SLOT=9a
yubico-piv-tool -k -s ${SLOT} -a generate \
  --pin-policy=once --touch-policy=cached --algorithm=ECCP384 \
  -o /tmp/${SLOT}-pub.pem

Generate a key in slot 9D.

NOTE: If you aren't planning to use this key for MacOS login, then you can skip configuration of slot 9D.

SLOT=9d
yubico-piv-tool -k -s ${SLOT} ... # as-above

Now you can either self-sign these certificates. ensure you perform this for all slots you generated keys in. You will need to enter your PIN and management key to authorise this.

SLOT=9a
yubico-piv-tool -a verify-pin -a selfsign-certificate -s ${SLOT} \
  --valid-days=7300 -S "/CN=PIV/" -i /tmp/${SLOT}-pub.pem -o /tmp/${SLOT}-cert.pem
yubico-piv-tool -k -a import-certificate -s ${SLOT} -i /tmp/${SLOT}-cert.pem

Or you can generate a CSR and submit it to a CA for signing. Once signed, you can import the certificate.

SLOT=9a
yubico-piv-tool -a verify-pin -a request-certificate -s ${SLOT} \
    -S "/C=AU/ST=Queensland/L=Outback/O=Farmin/CN=Stevo/" \
    -i /tmp/${SLOT}-pub.pem -o /tmp/${SLOT}-csr.pem

# If attestation is required, you need to also submit:
yubico-piv-tool -s ${SLOT} -a attest > /tmp/${SLOT}.attest.pem
yubico-piv-tool -a read-certificate -s f9 > /tmp/${SLOT}.attest.chain.pem

# The CA will sign and return the cert now.

yubico-piv-tool -k -a import-certificate -s ${SLOT}
    -i /tmp/${SLOT}-cert.pem

If this has been performed correctly, you should see the following:

yubico-piv-tool -a status
Version:	5.4.3
Serial Number:	18412815
CHUID:	3019d4e739da739ced39ce739d836858210842108421c84210c3eb34101a9673b0f5c0e802eb95e82fc814353a350832303330303130313e00fe00
CCC:	No data available
Slot 9a:
	Algorithm:	ECCP384
	Subject DN:	CN=PIV
	Issuer DN:	CN=PIV
	Fingerprint:	77b330ac8969b2e073fd89ca3eb429472143d857c17320130c5eeb5c42e4ab0a
	Not Before:	May 16 07:49:53 2024 GMT
	Not After:	May 11 07:49:53 2044 GMT
Slot 9d:
	Algorithm:	ECCP384
	Subject DN:	CN=PIV
	Issuer DN:	CN=PIV
	Fingerprint:	63219930432f508c14f199b83e83fd1bfc2592cfb5a556e8152e416b3429b273
	Not Before:	May 16 07:50:04 2024 GMT
	Not After:	May 11 07:50:04 2044 GMT
PIN tries left:	3

MacOS PIV Login

MacOS allows you to login to your machine and unlock your full disk encryption with your smartcard. This is why slot 9D is required, for decrypting the filevault key. The security differences of this compared to standard system login are left an an exercise to the reader however, but I'd like to think this is a pretty excellent way to authenticate to you Mac given the PIN bruteforce limits.

To allow a smartcard to authenticate a user it must be paired to the account.

You can list smartcards available for pairing with:

# sc_auth identities
SmartCard: com.apple.pivtoken:1A9673B0F5C0E802EB95E82FC814353A
Unpaired identities:
47937AF53DD028F1C338DFA6472D5AACD8D5A61E	Certificate For PIV Authentication (PIV)

The identity can be added for the user with:

sc_auth pair -u <local user name> -h <card hash>
sc_auth pair -u firstyear -h 47937AF53DD028F1C338DFA6472D5AACD8D5A61E

Inversely this can be undone with

sc_auth unpair -u <local user name> -h <card hash>

Alternately a pairing UI is available. You can enable it with:

sc_auth pairing_ui -s enable

By removing and reinserting your key, it will prompt you to enroll the smartcard.

You can (and should) have at least 2 smartcards paired to your account such that one can act as a backup.

Requiring Smartcard on MacOS

⚠️ WARNING: Requiring smartcard authentication on your device MAY cause your account to be locked out and your data to become inaccessible. YOU MUST be aware of this before proceeding. DO NOT enable this lightly!

To require a user can only authenticate with a smartcard you should review the apple documentation. Older documentation is also available.

It is important to note for Apple Silicon Macs that when a machine is shutdown, the last smartcard that was used for the user is the only smartcard that can unlock the disk. This means if you have two yubikeys/smartcards and you reboot, only the card that was last used can unlock the disk.

It may be more prudent on personal machines to set the user password to a long random password which can act as a recovery key rather than enabling smartcard only authentication. If you choose this, ensure you disable touch id for unlocking the machine.

SSH Keys

The key in slot 9A can be used for SSH. This is achieved with /usr/lib/ssh-keychain.dylib as a pkcs11 module, or the yubico pkcs11 module from /usr/local/lib/libykcs11.dylib. The primary difference between these two is that ssh agent forwarding only works with /usr/lib/ssh-keychain.dylib.

We want to note that generally ssh agent forwarding should be avoided due to potential security issues. Only perform ssh agent forwarding to machines you have high levels of trust in.

Configure /usr/local/lib/libykcs11.dylib

You will need to install and use ssh from brew. This is because libykcs11.dylib is not a system library, so it is not able to be loaded in the default MacOS ssh install as part of system integrity protection.

brew install openssh

In ~/.ssh/config set the value:

PKCS11Provider /usr/local/lib/libykcs11.dylib
IdentitiesOnly yes
IdentityFile ~/.ssh/id_ecdsa_piv.pub

Now list the keys on your device with ssh-keygen -D /usr/local/lib/libykcs11.dylib: One value should end with the text Public key for PIV Authentication. This is the line you should copy into ~/.ssh/id_ecdsa_piv.pub.

You can repeat the IdentityFile line and have multiple smartcards available.

Why do we need IdentitiesOnly yes?

Because libykcs11.dylib shows all keys and certificates on the smartcard, if we don't limit to IdentitiesOnly which we define in our IdentityFile declarations, then the smartcard can send 4 keys to a remote host at once. If you have multiple cards attached you can send many more keys. Some hosts will reject authentication from clients that send more than 5 possible certifictes at once, which can cause authentication failures.

Configure ssh-keychain.dylib

In ~/.ssh/config set the value:

PKCS11Provider /usr/lib/ssh-keychain.dylib
IdentitiesOnly yes
IdentityFile ~/.ssh/id_rsa_piv.pub

ssh-keychain.dylib only works with keys in slot 9A, so you can create the .pub file with:

ssh-keygen -D /usr/lib/ssh-keychain.dylib > ~/.ssh/id_rsa_piv.pub

To use ssh-agent now, you will need an ask pass application. We recommend the askpass script by theseal which can be installed with brew.

brew install theseal/ssh-askpass/ssh-askpass

Now clear the agent first with:

ssh-add -e /usr/lib/ssh-keychain.dylib

Then add our smartcard to the agent

ssh-add -cs /usr/lib/ssh-keychain.dylib

You should now be prompted for your PIN. After this, the ssh-agent should work as expected, and can be used for agent forwarding if you desire.

Again, be warned that ssh agent forwarding should only be performed to hosts you absolutely trust.

Acknowledgements

Thanks to Micolous for working with me on this guide