DEV Community

Cover image for Building a Personal Zero Trust Workplace with Hardware Keys
Oleksiy Pototskyy
Oleksiy Pototskyy

Posted on

Building a Personal Zero Trust Workplace with Hardware Keys

A practical guide to building a zero-trust environment with hardware keys - using YubiKey for encryption, SSH access, and multi-factor authentication.

Introduction

In my work I spend most of my time inside enterprise and regulated environments like banking, finance, European institutions, and other critical services. These environments have strong rules and very high security expectations. In such places even a small mistake with access rights can lead to serious risk. I decided to take personal responsibility for my security practices and organize my own access model in a way that makes account misuse close to impossible.
I don’t want to rely only on company controls or default identity management rules. My personal goal is to make sure that my accounts can never be used for intrusion, even if a password is stolen or if my laptop is compromised. To achieve this, I started to build a personal standard, a method that I can use in every project or client engagement. I also want to share this approach with other professionals through this article, because I believe many people face the same questions but do not always know how to structure the answers.

My Approach

The first decision I made was to use hardware keys as the center of trust. I chose YubiKey 5 FIPS Series because it is validated for strict environments and trusted by many governments and banks. I also decided not to rely on only one key. I carry a primary key for daily work and I keep a second one as backup. If one is lost, I can continue without delay.
It’s important to mention that this article is not an advertisement for Yubico or any brand. This is simply my personal approach and the toolset I chose. You can use any hardware security key that fits your workflow - several vendors follow the same open standards and achieve similar protection. In some of projects, I use specialized or higher-assurance hardware tokens specifically designed for classified or regulated environments.
To make recovery possible, I also prepared an encrypted USB flash drive, like iStorage datAshur PRO FIPS 140-2 Level 3 certified, with copies of important private keys. This USB is encrypted with strong protection and stored in a safe location. My plan is simple - hardware keys for everyday security, and encrypted offline storage as last-resort recovery.
I plan to store all credential on the device and never leave them in plain text. For login, I prefer phishing-resistant methods like FIDO2 or PIV. For code signing and SSH, I want to use subkeys that stay inside the hardware token. Each key is tied to one clear role or account, so I can use different credentials for different clients or environments.
By registering both YubiKeys with all systems - identity providers, git repositories, cloud accounts, etc., I remove the single point of failure. If one key is damaged or lost, I use the other one without needing helpdesk reset.
So, let’s configure everything step by step.

Using GPG Keys and YubiKey for Secure Identity

I decided to build my system around GPG keys stored on a YubiKey, because this combination allows full control of identity, clear separation between master and working keys, and physical protection of private data. The GPG model gives you one master key (used only for certification) and several subkeys for daily tasks such as signing and encryption. The YubiKey stores these subkeys in hardware, so they never leave the device. Even if your computer is compromised, the private keys stay safe.

Building the GPG Key Structure

First, I make sure my tools are ready. I install GnuPG and YubiKey Manager. On macOS, Homebrew makes it easy.

# Install tools 
$ brew install gnupg
$ brew install ykman
Enter fullscreen mode Exit fullscreen mode

Then I create my master key offline. This key is used only to certify and manage subkeys - it never touches the Internet. I generate it once on a trusted machine and keep it on an encrypted USB drive. It is my root of trust.

$ gpg --expert --full-generate-key
# Choose (11) ECC (set your own capabilities)
# Keep only "Certify (C)" enabled
# Select NIST P-256 (or P-384 for FIPS)
# Add name and main email, set expiry
Enter fullscreen mode Exit fullscreen mode

After that, I add all my professional emails as additional user IDs (UIDs). This way, one identity covers several addresses, so I can sign commits and emails from different domains without maintaining separate key pairs.

$ gpg --edit-key <FPR>
gpg> adduid    # repeat for each email
gpg> save
Enter fullscreen mode Exit fullscreen mode

Then I create two subkeys - one for signing and one for encryption.

$ gpg --edit-key <FPR>
gpg> addkey    # ECC (set your own capabilities) -> Sign (S)
gpg> addkey    # ECC (set your own capabilities) -> Encrypt (E)
gpg> save
Enter fullscreen mode Exit fullscreen mode

Now I have Master key (Certify-only), Signing subkey (S) and Encryption subkey (E).

Creating the Backup

Before moving anything to the YubiKey, I take a full backup. This is critical, because after the move, the private parts of the subkeys will be deleted from the local system.

$ gpg --armor --export-secret-keys <FPR> > /USB/master-and-subkeys.asc
$ gpg --armor --export <FPR> > /USB/public.asc
$ gpg --output /USB/revoke-<FPR>.asc --gen-revoke <FPR>
Enter fullscreen mode Exit fullscreen mode

These three files - private keys, public key, and revocation certificate, are my safety net. I store them on an encrypted USB drive that stays offline.

Moving Keys to YubiKey

The move to hardware is what makes this setup different. Once the subkeys live on the YubiKey, the private material cannot be extracted. Each signing or decryption operation requires a PIN and a physical touch.

$ gpg --edit-key <FPR>
gpg> key 1
gpg> keytocard   # (1) Signature key
gpg> key 2
gpg> keytocard   # (2) Encryption key
gpg> save
Enter fullscreen mode Exit fullscreen mode

When I run “gpg --card-status”, I see that both subkeys are now inside the YubiKey. My laptop only holds small stubs that point to them. If I lose this laptop, the keys are still safe.

Testing the Setup

To confirm everything works, I create a simple signature and decryption test:

$ echo "test" > message.txt
$ gpg --clearsign message.txt      # sign
$ gpg --verify message.txt.asc     # verify
$ echo "secret" | gpg -ear you@example.com > secret.asc
$ gpg -d secret.asc                # decrypt
Enter fullscreen mode Exit fullscreen mode

The YubiKey light blinks, asking for a touch.
The operation completes successfully, proving that hardware signing and decryption are active.

Using the Same YubiKey on Another Laptop

One of the strengths of this model is portability. I can plug the same YubiKey into another system and start working immediately. The private subkeys remain on the device and the new computer only needs the public key and GPG installed.
On a new laptop, I simply import my public key:

$ gpg --import public.asc
$ gpg --card-status
Enter fullscreen mode Exit fullscreen mode

This command links the YubiKey to the local GPG setup and creates stubs automatically. I mark the key as trusted:

$ gpg --edit-key <FPR>
gpg> trust
# choose 5 = ultimate
gpg> save
Enter fullscreen mode Exit fullscreen mode

That’s it. The same YubiKey can now sign and decrypt on both machines, and no secret material was ever copied between them.

How It Works

This solution gives me three wins at once. First, I keep a single identity that is simple to manage, but it still supports many email addresses. Second, I reduce risk because the private keys are inside the hardware key and not on disk. Third, I have a clean recovery solution - I have an offline master and an encrypted backup, so I can rotate or rebuild without panic. This is why I use this method in my personal environment. It is safe, it is simple, and it is fast enough for daily work. If a client needs stronger separation, I switch to a separate hardware key and a separate GPG key for that client, but the flow stays the same and the commands do not change much.

Hardware-backed SSH Keys for Secure and Segmented Access

After I finished organizing my GPG setup, I wanted to extend the same logic to SSH authentication. I work with different companies and regulated infrastructures, where each environment must stay isolated. My goal was to remove any chance that a single compromised system could lead to unauthorized access to another. For this, the YubiKey FIDO2 interface became the most natural center of my SSH strategy. It lets me create separate, hardware-backed SSH identities for every organization I work with, while keeping all private material locked inside the key.
SSH keys are one of the oldest security tools in daily technical life, but in practice they’re often left unmanaged, copied across systems, and reused for years. This approach doesn’t fit in a modern enterprise environment where auditability, compliance, and key rotation matter. By placing all my SSH credentials inside the YubiKey, I can enforce physical presence, reduce exposure, and remove the need to trust any single laptop or server with private keys.

Building the FIDO2 SSH Key

The FIDO2 specification adds hardware-backed signing to OpenSSH through special key types that end in “-sk”. Each time I generate one, the YubiKey produces and stores the private component internally, and only a small key handle remains on disk. Even if the laptop is lost, the private key never leaves the hardware.
On macOS, I installed the full FIDO2-enabled OpenSSH from Homebrew, because Apple’s default build doesn’t include FIDO2 support. After that, the process was simple and repeatable.
For each client, I create a new key using a clear comment and file name:

$ ssh-keygen -t ed25519-sk -C "oleksiy.pototskyy@client-name.com" -f ~/.ssh/id_ed25519_sk_clientname -N ""
Enter fullscreen mode Exit fullscreen mode

During generation the YubiKey asks me to touch it to confirm. The system creates two small files - one private stub and one public key. The private stub only references the key stored on the YubiKey and it doesn’t contain secret data.
I then copy the public part to the remote system’s authorized_keys file, or upload it to a Git service. From that point on, every SSH login requires physical confirmation on the YubiKey. No password and no local private key are involved.

Using PIN or Touch-only Modes

When generating the key, OpenSSH allows an optional flag “-O verify-required”. If I include it, the key enforces both PIN and touch for every authentication, creating a real two-factor flow. If I omit it, authentication requires only a touch. For high-security environments, I keep the verify option enabled but for quick access in safe contexts, I use touch-only keys. Both remain fully hardware-protected, and the flexibility is ideal when working with several clients under different compliance levels.

Organizing Keys per Environment

Each organization gets its own hardware identity. I simply repeat the command above, changing the label and file name. The YubiKey can store hundreds of such FIDO2 credentials, each one unique. This isolation means I can revoke a single key for one client without touching others.
In my ~/.ssh/config, I group them by host so they’re easy to use:

Host clientA
    HostName ssh.clientA.net
    User mylogin
    IdentityFile ~/.ssh/id_ed25519_sk_clientA
    IdentitiesOnly yes
Enter fullscreen mode Exit fullscreen mode

Now I can just type ssh clientA and the right key is used automatically. When I log in, I see the small LED blink, I touch the YubiKey, and access is granted.

Portability and Recovery

Because the private material never leaves the YubiKey, I can use the same device on any machine. All I need is OpenSSH 8.2 or newer.
When I plug the YubiKey into a new laptop, I can rediscover my stored credentials with:

$ ssh-keygen -K
Enter fullscreen mode Exit fullscreen mode

and the system rebuilds the local key stubs automatically. There’s nothing to export or copy, only the hardware matters.
If I ever buy a second YubiKey as a backup, I simply generate new FIDO2 keys for each client and upload their public parts again. FIDO2 credentials are designed to be non-cloneable.

How It Works

This configuration gives me complete separation between clients while keeping my workflow simple. Each SSH key is physically protected, auditable, and easy to rotate. The YubiKey handles the cryptography and OpenSSH handles the connection. No private key touches the file system, and no password is reused. Even if my laptop is lost, nothing inside it can be used to access a remote system.
For professionals who move between projects, companies, or cloud infrastructures, this approach offers a clear and secure mental model - each environment has its own ssh key and all ssh keys protected by one device. It turns a common administrative task into a predictable, hardware-anchored process that can scale safely across clients and years.

Multi-factor authentication (MFA)

In addition to my SSH and GPG configuration, I also decided to use my YubiKey as a multi-factor authentication (MFA) method to secure access to my main accounts and administrative tools.
It now protects my identity in Okta, GitHub, and even the admin zone of my personal website. This step prevents unauthorized access even if one of my passwords were somehow exposed. It also adds a physical layer between my credentials and the outside world - a small extra step that makes a large difference.

Zero Trust Security

Still, even with hardware-based authentication, I never rely only on encryption or cryptographic strength. Security is not about trusting one mechanism, it’s about reducing exposure at every level.
That’s why I always restrict access to critical systems by IP and, whenever possible, keep services off the public Internet entirely. Strong encryption protects the channel, but isolation protects the surface. The combination of both is what creates real resilience in practice.

Summary

At this stage, my environment feels stable and complete. The YubiKey now serves as a single hardware anchor for all core operations - code signing, message encryption, and secure logins. It removed the need for local secrets, simplified my routine, and gave me a consistent way to verify that every sensitive action is intentional and confirmed by touch. This approach already covers the most important part of my daily work - securing communications, repositories, and infrastructure access.
Still, I look forward to expanding its use in the future. There are more capabilities in the YubiKey ecosystem that I haven’t yet activated - features like OATH one-time passwords, PIV smart-card certificates, and FIDO2 passwordless login for cloud dashboards and developer portals. Each of these can strengthen a specific area of identity management, and I plan to integrate them gradually as I see clear benefits. For example, OATH support can easily replace mobile authenticator apps, keeping two-factor secrets inside the key instead of my phone. The logic stays the same - the fewer secrets that live on my laptop or phone, the safer my overall environment becomes.
But for now, I prefer to keep my setup simple. Hardware-backed signing and hardware-backed authentication are already enough for a strong and maintainable security posture. Every additional feature will come naturally when it’s justified by real need. Security works best when it grows from clear reasoning, not from feature accumulation.

References:

Top comments (0)