2

The use case I'm looking for is that I walk up to a headless server and "unlock" it using a hardware key, where scripts on the server recognize that I've plugged it in and automatically use it without a pin or password or additional factors.

The most primitive way of implementing this would be to have a USB thumb drive with unencrypted raw AES keys on it which the scripts on the server find and use to decrypt things. The downside is that the USB stick could be copied and there is no way to revoke it if it were lost. It also runs into trouble with possible filesystem corruption of the USB stick itself if the drive were removed when the scripts were still using it.

It seems like a smartcard or Yubikey would be the obvious solution to these problems, but it also seems like most people describing Yubikey solutions pair it with gpg as a second factor of auth. I don't want "extra" auth factors, I want the key to be one of multiple possible decryption methods. I don't want to have to configure gpg on each host or have "identities" or expiration dates or trust chains or any of that. The other popular option is to integrate it with LUKS, but I was hoping for a more non-root userland option.

I just want to take an encrypted AES key and directly ask the Yubikey to decrypt it with an RSA private key that lives in hardware (without entering a pin or password, but a short touch or long touch on the device is ok).

Is there any existing tool that can accomplish this? Scripting language libraries are fine too.

4
  • @PhilipCouling The primary function of a Yubikey is to act as an authenticator to a back-end who validates the one-time-password sent by the device. But, the Yubikey 5 also has a smartcard feature that can be given pre-existing RSA keys (such as from gpg), or be asked to generate its own. I don't have proof, but I'm quite sure requesting a RSA decryption is possible. I just can't find a command to do it... Commented Jun 20, 2023 at 23:50
  • I'm familiar with at least two ways to put RSA keys on a Yubikey - via X.509 certificate/key pairs with the PIV applet, and via OpenPGP key pairs. Both can assist the respective decryption of cyphertext formatted the appropriate standard (PGP or X.509, both typically used in emails. AFAIR they both require PINs of some sorts, even if just the default one. Is it impossible in your use case to hardcode the PIN? Commented Jun 21, 2023 at 10:18
  • @chexum A hard-coded pin is ok. My main objection to gpg was the deep integration required, which makes the script messy and complicated, wasting my time and that of anyone who tries to understand what I wrote. Also, I couldn't tell if you can pair a Yubikey with two different gpg environments, and I might want to set it up for normal gpg usage on my laptop, and I don't want that linked to the config of the servers. Commented Jun 21, 2023 at 16:13
  • AFAIK PGP (gpg) has inbuilt an element of automatic key selection. That's what the email addresses are about. I don't have a Yubikey to experiment, but the setup of GPG sugests you can easily "pair a Yubikey with two different environments". From the face of it, gpg-agent is just using the Yubikey as an additional [pluggable] store of keys, and offloading the decryption work to the physical yubikey. In theory you should be able to plug it into environment with gpg installed and the right drivers and those keys become available until it is unplugged again. Commented Jun 22, 2023 at 12:00

2 Answers 2

3

Assuming you are running on a modern Linux or similar system, the encryption part should be fairly simple.

(Edit: Reading your question again, I see you started by asking about authentication and then asked the specific question about RSA encryption which I've answered here. You probably want to set things up as I've described here, then go and investigate pam_pkcs11 which is designed to do this kind of thing.)

Setup

Make sure your Yubikey has enabled 'PIV' mode, and you've installed the OpenSC PKCS#11 module which can talk to it. Also install the OpenSSL PKCS#11 'engine'.

Use the yubico-piv-tool or any other method you like to install an RSA key into the slot of your choice, let's say the 'Card Authentication slot 9e for example. You can either import an existing key, or create one that's never existed outside the Yubikey.

Signing

Now it's simple. Any software that can use an RSA key from a file ought to accept a standard PKCS#11 URI identifying the key in the Yubikey instead.

The URI for the key in the Card Authentication slot will be something like 'pkcs11:manufacturer=piv_II;id=%04'.

So you can do something like

 echo "test payload" | openssl dgst -sha256 -sign 'pkcs11:manufacturer=piv_II;id=%04' -hex 

... except OpenSSL doesn't quite get this right yet, so you have to add

 -engine pkcs11 -keyform engine 

to the above command line because all crypto software authors hate their users and it doesn't bother to infer those obvious things from the fact that you gave it a PKCS#11 URI. This is also slightly out of date because newer OpenSSL uses "providers" instead of engines, and I think it still doesn't just get things right for itself so you may need a slightly different pointless arcane incantation to make it do so.

Decent software won't need those extra hints, and will just take the URI in place of a filename and do the right thing. File bugs if not.

Example

 $ yubico-piv-tool -s9e -ARSA2048 -agenerate | tee pubkey.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr9LrzjNbRABqhDQrGi3l VcQhmUu0lls8k4XnO8c/U0oS6IvH4H7HuqXFfwThYofxIgA2eIXuRXf+V/CSWtXN 40Bb10QKcTXVATm05+KFNWg1GCVg2yrvsUOQSd6MOxAa5goUUi2xOjeLFZRvXuvt YmLytwY77YqE0WOHYfYuk9kolueZHhq4BSOVRmQpZxKd6/MkWlT46SPc3Bwbyx41 t2U42vlnHYuma3NF6qI+a+LaMyvkFVBkRM6A1WB2u5jjl/ZQmyYsuqg2e8xu7P8m 5/GUH9HjD074+ea1NEdMncPhKjO+pL24BSebtIPUmzEJIh6kCaweJYiMKYuLx15H HQIDAQAB -----END PUBLIC KEY----- Successfully generated a new private key. $ echo Test | openssl pkeyutl -encrypt -pubin -inkey pubkey.pem > Test.enc $ openssl pkeyutl -decrypt -in Test.enc -engine pkcs11 -keyform engine -inkey 'pkcs11:manufacturer=piv_II;id=%04' Engine "pkcs11" set. Test 

You can also do signatures...

 $ echo Test | openssl dgst -sha256 -sign 'pkcs11:manufacturer=piv_II;id=%04' -engine pkcs11 -keyform engine > signature.bin $ echo Test | openssl dgst -sha256 -verify pubkey.pem -signature signature.bin Verified OK 

TL;DR

The different slots in the Yubikey PIV have different policies, especially around whether the PIN is required. Read the documentation I linked. See https://www.infradead.org/openconnect/pkcs11.html for more hints on finding the URI for a given object in a token.

11
  • (Someone commented that my example uses signatures not encryption; can't see that comment now...) Actually for RSA at the low level they are the same. RSA signature is decryption and verification is encryption. If you spy on the commands which get sent to the card for the above signature, the literal command is probably a Decrypt. But yes, as I said, the example uses signatures but encrypt/decrypt is similar. And all of the actual answer applies to both cases. Just identify your RSA key in the card and use its URL anywhere you'd use a filename. Commented Jun 23, 2023 at 7:27
  • Good point about EC; that comment was mostly instinct. I don't think we want to get into the complexities of ECDH and HKPE-like stuff which would let you use EC for encryption at rest, so I'll just remove that. RSA operations really are slow on smartcards and TPMs though. There are a few operations to be done: hash, PKCS#1 v1.5 or similar padding, then the actual decryption. Some keys and tokens don't support the first two operations; some insist on doing it for themselves. I may have chosen a Yubikey slot which doesn't support raw decrypt; testing now... Commented Jun 23, 2023 at 8:04
  • 1
    It's for TPM not PKCS#11 but here's an example of alternative code paths for Sign. vs. Pad+Decrypt. Fairly sure I tested them as being equivalent within the last week or so. github.com/dwmw2/rolesanywhere-credential-helper/blob/tpm/… Commented Jun 23, 2023 at 8:35
  • 1
    "Your key and mine". That's the point in the transient "mine" key in HKPE. Just make one up. Store its public key alongside the encrypted message, and you're done. ECDH is a method whereby you can use two pubkeys and just one of the privkeys to generate the actual encryption key. Your own EC public/private key are permanent. Anyone can just make up pub/private key pair, encrypt a message, then throw away the transient private key. As long as they store the public key with the encrypted message, you will always be able to decrypt it. Commented Jun 23, 2023 at 8:41
  • 1
    The "providers" system in OpenSSL 3.x is a bit different; you no longer need to specify 'keyform', although you do still need to load the provider. (Additionally, it is possible to load the pkcs11 provider system-wide via openssl.cnf, though this might still have some unpleasant side effects, but I think most of those have been worked out and pkcs11-provider is now no longer as eager to load modules as it used to be.) Commented Jun 23, 2023 at 10:11
1

Disclamer: I've never used a Yubikey

Some background

It looks like Yubikey supports a number of protocols including OpenPGP. However it does not claim to support "plain old" RSA. It's not impossible that somewhere in the depths of the IO, a Yubikey might be asked to RSA decrypt something, but this does not appear to be publicly documented.

Instead it appears that to all intents and purposes, a Yubikey directly implements a number of protocols, most of which are focussed on identity verification (not what you want).

This may actually be for good reason. AFAIK experts have decided that it's a bad idea to encrypt large quantities of data with RSA. Instead the major protocols typically encrypt bulk data with a symmetric key and then encrypt the [much smaller] symmetric key with RSA.

There's a lot you can do wrong with encryption, and you'd never know until someone abused the mistake, so inventing your own protocol is generally a bad idea.

Encrypting and decrypting data with Yubikey

While this is not technically what you've asked for it seems to achieve your goal. Namely you want to be able to encrypt something that can [only] be decrypted with the help of your hardware Yubikey.

OpenPGP is a well established encryption protocol which can be used to encrypt files using RSA. Technically it encrypts the file with a symmetric key and then encrypts the symmetric key with RSA. It's also readily available on most *nix OS.

Yubikey supports OpenPGP (AKA gpg) and this can be used on its own, not just as 2FA. More accurately Yubikey can be used to store your RSA Private key securely and, unlike storing it on a regular USB pen drive, it can help decrypt an OpenPGP encrypted file without exposing the private key to the host machine!

gpg-agent is a backend for handling gpg keys see here. gpg-agent should be able to directly interact with a Yubikey to decrypt a file See Here:

The gpg-card is used to administrate smart cards and USB tokens. It provides a superset of features from gpg --card-edit an can be considered a frontend to scdaemon which is a daemon started by gpg-agent to handle smart cards.

There's a blog post "Using your Yubikey to store your SSH Key (RSA 4096)" which explains how to ask gpg to store an RSA key on a Yubikey. The important line to watch out for is:

gpg> keytocard 

... also documented here

From there all you need to do is just encrypt or decrypt whatever data you want to using

# Recipient identifies which public key to encrypt with gpg --encrypt <filename> -r <recipient ... you> gpg --decrypt <filename> 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.