Using somewhat obscure MacOS tooling to generate SSH keys
I’ve recently been on a little side-quest to use biometric-backed SSH keys in more places.
This little rabbit hole came out of my work earlier this year on implantable SSH credentials, and some technology we’ve been experimenting with at work. Honestly, I think I’ve spent a bit more time thinking about SSH this year than a person reasonably should.
Up until this week, I had only really solved secure key storage for mobile devices. Which is made easy with the help of Yubikeys, and FaceID-backed SSH credentials through mobile apps like Terminus.
To get biometric-backed keys on my MacOS devices I turned to Secure Enclave, Apple’s TPM-like secure computing module. The SEP (Secu…
Using somewhat obscure MacOS tooling to generate SSH keys
I’ve recently been on a little side-quest to use biometric-backed SSH keys in more places.
This little rabbit hole came out of my work earlier this year on implantable SSH credentials, and some technology we’ve been experimenting with at work. Honestly, I think I’ve spent a bit more time thinking about SSH this year than a person reasonably should.
Up until this week, I had only really solved secure key storage for mobile devices. Which is made easy with the help of Yubikeys, and FaceID-backed SSH credentials through mobile apps like Terminus.
To get biometric-backed keys on my MacOS devices I turned to Secure Enclave, Apple’s TPM-like secure computing module. The SEP (Secure Enclave Processor) is a dedicated processor that can perform cryptographic operations outside of the main CPU, and allows userspace applications to effectively encrypt arbitrary data against it.
While some implementations of SEP-backed SSH keys work by using Secure Enclave to decrypt an on-disk SSH private key, I’m more interested in using the Secure Enclave’s own key as an SSH private key. This functionality became possible with an update to the CryptoTokenKit in MacOS 26.
Generating a keypair
Modern versions of MacOS ship with a helper script called sc_auth. This script contains some commands (things labeled ctk) that allow users to generate a “virtual smartcard”, stored in the system’s Secure Enclave.
This virtual smartcard can be assigned a “key type” that’s SSH compatible, for example p-256-ne, which OpenSSH calls a sk-ecdsa-sha2-nistp256@openssh.com key.
To create the virtual smartcard (ctk identity), a user can run this command:
sc_auth create-ctk-identity -k p-256-ne -t bio -l 'My SSH Key'
Note that -k is the key type (there are a couple options), -t bio enables TouchID, and -l sets the key’s Common Name.
Using a key stored in Secure Enclave
Once a ctk identity is created, it can be viewed with the sc_auth helper script:
$ sc_auth list-ctk-identities
Key Type Public Key Hash Prot Label Common Name Email Address Valid To Valid
p-256-ne C556BD23D1D63C24F1E007A643E25776BB3D371D bio My SSH Key My SSH Key 2026-12-24, 20:09 YES
Note that the identity has a “Valid To” field. I’ve confirmed that this is not enforced by OpenSSH, and can be ignored. From my documentation scouring, it appears that this field is only used for ctk-backed certificates, not ECDSA keys.
To load the ctk identity as an SSH Resident Key, the following commands can be used:
cd ~/.ssh
SSH_ASKPASS_REQUIRE=force SSH_ASKPASS=true ssh-keygen -w /usr/lib/ssh-keychain.dylib -K
A few things are going on here:
ssh-keygenlikes to dump its output in the current working directory, so you should make sure you are in~/.sshbefore running it.- OpenSSH needs to be instructed to talk to
ssh-keychain.dylibin order to pull the key from Secure Enclave. - I recommend setting the askpass variables to stop ssh-keygen from asking you for a password, as it doesn’t actually use the password that you might try to give it.
Now that the key has been “downloaded”, you’ll find the public key in the usual place, and can add it to authorized_keys on a remote system.
$ cat ~/.ssh/id_ecdsa_sk_rk.pub
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBDQ3h1E/XNEszgtX9ybDRhmFvIlnRdKnXDJM/CNnZP6a9FXEKlmN+q3cXhEaOuGeZ5b2JepsGY5fncwefAiZI3gAAAAEc3NoOg== ssh:
Finally, with the key placed in ~/.ssh and the public key loaded onto a remote system, you can use SSH like normal:
ssh -o SecurityKeyProvider=/usr/lib/ssh-keychain.dylib user@myhost.example.com
If you don’t like setting SSH client options on the command line (I sure don’t), you can always set the SecurityKeyProvider option in your ~/.ssh/config, or it can be configured with an environment variable: export SSH_SK_PROVIDER=/usr/lib/ssh-keychain.dylib