Generate hardware-backed SSH authentication keys with a YubiKey. Version: 1.1.0. Copyright (C) 2024 Alaska Center for Energy and Power, University of Alaska Fairbanks. License: For UAF Use Only.
- Bash, OpenSSH client (
sshandssh-keygen) with FIDO support, and YubiKey Manager (ykman). - OpenSSH 8.2+;
--verify-requiredrequires 8.4+. - Default
ed25519-sk: FIDO2 enabled and YubiKey firmware 5.2.3+. ecdsa-sk: compatible hardware with FIDO U2F or FIDO2 enabled.- Resident credentials and PIN verification additionally require hardware support for the selected feature. Set a FIDO2 PIN before using these options.
On Debian/Ubuntu:
sudo apt install openssh-client yubikey-managerOn other systems install OpenSSH with FIDO support and YubiKey Manager through an appropriate package manager. The script uses portable text parsing, but hardware access and OpenSSH builds differ by platform.
Connect only the intended FIDO authenticator, including disconnecting other
brands of security keys. The script rejects multiple YubiKeys, including those
without serial numbers. It intentionally does not offer serial-based device
selection: selecting a device in ykman would not also select it in ssh-keygen.
Keep the device configuration unchanged during generation.
# Preview a PIN-verifying key without creating files or credentials:
./ssh-keygen-mfa-yubikey.sh --verify-required --dry-run work
# Generate it (touch the key and enter the PIN when prompted):
./ssh-keygen-mfa-yubikey.sh --verify-required work
# Optionally make a credential recoverable on another computer:
./ssh-keygen-mfa-yubikey.sh --verify-required --resident portable-work
# Explicit compatibility option for older hardware:
./ssh-keygen-mfa-yubikey.sh --type ecdsa-sk legacy-work
./ssh-keygen-mfa-yubikey.sh --help-v, -r, and -t are short forms of --verify-required, --resident, and
--type. Plain invocation preserves the previous touch-based behavior; it does
not require PIN verification for signing unless --verify-required is given.
A passphrase for the local key handle is separate from the authenticator PIN.
Keys are saved under ~/.ssh. Names must start with a letter or digit and contain
only letters, digits, dots, underscores, and hyphens. Without a name, the script
uses hostname, date, and serial number, or a security-key suffix for devices
without serial numbers. Use distinct explicit names for multiple serial-less
keys or repeated generation on the same day.
The script refuses existing private/public destination files, creates ~/.ssh
with mode 700 if absent, and reports success only when ssh-keygen succeeds.
Existing directory permissions are preserved. A dry run still reads device
information; it cannot fully verify PIN setup or hardware/provider support.
Generation errors from OpenSSH remain visible. Failed resident enrollment may
leave a credential on the device; inspect it before retrying instead of assuming
all hardware changes were rolled back.
Install the .pub file in the target account's ~/.ssh/authorized_keys.
For an SSH alias, add to your local ~/.ssh/config:
Host work-server
HostName server.example.org
User your-username
IdentityFile ~/.ssh/work
IdentitiesOnly yesFor server-enforced verification, prefix the public key's authorized_keys entry
with verify-required on a supporting OpenSSH server. This ensures the server
checks user verification instead of relying solely on the client's key options.
Resident credentials use the application label ssh:<key_name>. To retrieve
key handles on another computer, connect the key and run ssh-keygen -K in a
private directory (OpenSSH 8.3+).
56B8
This retrieves handles, not an exportable copy
of the hardware's private key. Resident storage improves portability; it is
optional and does not replace PIN verification.
bash -n ssh-keygen-mfa-yubikey.sh
shellcheck ssh-keygen-mfa-yubikey.sh
python3 -m unittest discover -s tests -vThe regression tests mock device discovery and key generation; they do not access real authenticators or generate credentials.
References: Yubico SSH guidance and OpenSSH ssh-keygen.