0

I'm trying to use a yubikey for encrypting/decrypting strings in my application. By now, I wrote some methods for testing purposes. My code was based on examples I found in the official repository: Yubico.YubiKey/examples/PivSampleCode/.

However, I'm not able to decrypt anything. I'm getting an InvalidOperationException: Referenced data or reference data not found.

I also notice that every time I run the method the encrypted string changes, is that right? I need to be able to encrypt with the internal private key and share the public key. But, if every time the key pair change is going to be a huge issue for me. Do you guys have any suggestions?

Here are the codes I'm using, both encrypt but not decrypt:

using System.Diagnostics;    
using System.Security.Cryptography;
using Yubico.YubiKey;
using Yubico.YubiKey.Cryptography;
using Yubico.YubiKey.Piv;
using Yubico.Core.Buffers;

private static bool KeyCollectorPrompt(KeyEntryData keyEntryData)
        {
            switch(keyEntryData.Request)
            {
                case KeyEntryRequest.AuthenticatePivManagementKey:
                    keyEntryData.SubmitValue(Hex.HexToBytes("010203040506070801020304050607080102030405060708").ToArray());
                    return true;
                case KeyEntryRequest.VerifyPivPin:
                    keyEntryData.SubmitValue(Encoding.ASCII.GetBytes("123456"));
                    return true;
            }
            return false;
        }

private void testEncryptionButton1_Click(object sender, EventArgs e)
        {
            IYubiKeyDevice yubikey = YubiKeyDevice.FindAll().First();
            string plainText = "helloWorld";
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            byte[] decryptedData = Array.Empty<byte>();
            byte[] encryptedDataBytes = Array.Empty<byte>();
            using (PivSession pivSession = new PivSession(yubikey))
            {
                try
                {
                    pivSession.KeyCollector = KeyCollectorPrompt;
                    PivMetadata pivMetadata = pivSession.GetMetadata(PivSlot.Attestation);
                    PivPublicKey pivPublicKey = pivMetadata.PublicKey;
                    using (RSA rsa = (RSA)KeyConverter.GetDotNetFromPivPublicKey(pivPublicKey))
                    {
                        encryptedDataBytes = rsa.Encrypt(plainTextBytes, RSAEncryptionPadding.OaepSHA256);
                        Debug.WriteLine("Encrypted Data: " + Convert.ToBase64String(encryptedDataBytes));
                        rsa.Dispose();
                    }
                    //this is the line that generates the exception, I tried with different slots.
                    byte[] rawDecryptedData = pivSession.Decrypt(0x9D, encryptedDataBytes);
                    int digestAlgorith = RsaFormat.Sha256;
                    RsaFormat.TryParsePkcs1Oaep(rawDecryptedData, digestAlgorith, out decryptedData);
                    Debug.WriteLine("Decrypted Data: " + Encoding.ASCII.GetString(decryptedData));

                    pivSession.Dispose();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }
        }

private void testEncryptionButton2_Click(object sender, EventArgs e)
        {
            if (yubikeysFoundComboBox.SelectedIndex == -1)
            {
                MessageBox.Show("You must select a Yubikey");
                yubikeysFoundComboBox.Focus();
                return;
            }
            IYubiKeyDevice yubikey;
            int serialNumber = Convert.ToInt32(yubikeysFoundComboBox.SelectedItem);
            if (!YubiKeyDevice.TryGetYubiKey(serialNumber, out yubikey))
            {
                MessageBox.Show("ERROR: Unable to set the selected Yubikey");
                yubikeysFoundComboBox.SelectedIndex = -1;
                yubikeysFoundComboBox.Focus();
                return;
            }
            yubikey.SetEnabledNfcCapabilities(YubiKeyCapabilities.Piv);
            Debug.WriteLine(yubikey.EnabledNfcCapabilities);
            Task.Delay(1000).Wait();
            yubikey = YubiKeyDevice.FindAll().First(y => y.SerialNumber == serialNumber) as IYubiKeyDevice;
            Debug.WriteLine(yubikey.EnabledNfcCapabilities);
            using (PivSession pivSession = new PivSession(yubikey))
            {
                try
                {
                    pivSession.KeyCollector = KeyCollectorPrompt;
                    //PivPublicKey publicKey = pivSession.GenerateKeyPair(PivSlot.Authentication, PivAlgorithm.Rsa2048);
                    
                    //Debug.WriteLine("Public Key: " + Hex.BytesToHex(publicKey.PivEncodedPublicKey.ToArray()));
                    //Task.Delay(200).Wait();

                    string plainText = "helloWorld";
                    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

                    byte[] signature;
                    int keySizeInBits = PivAlgorithm.Rsa2048.KeySizeBits();
                    byte[] digest = MessageDigestOperations.ComputeMessageDigest(plainTextBytes, HashAlgorithmName.SHA256);
                    int digestAlgorithm = RsaFormat.Sha256;
                    digest = RsaFormat.FormatPkcs1Pss(digest, digestAlgorithm, keySizeInBits);
                    string encryptedData;
                    signature = pivSession.Sign(0x9A, digest);
                    encryptedData = Convert.ToBase64String(signature);
                    Debug.WriteLine("Encrypted data: " + encryptedData);
                    Task.Delay(100).Wait();
                    //This is the line that throws the exception
                    byte[] rawDecryptedData = pivSession.Decrypt(0x9D, digest);
                    byte[] decryptedData = Array.Empty<byte>();
                    RsaFormat.TryParsePkcs1Decrypt(rawDecryptedData, out decryptedData);
                    Debug.WriteLine("Decrypted data: " + Encoding.UTF8.GetString(decryptedData));

                    pivSession.Dispose();
                }
                catch(Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }

        }

Do you guys, have any suggestions?

Thanks in advance!

pshore90
  • 1
  • 1
  • Do never us RSA to encrypt arbitrary plain text data. RSA by it's design can only handle data that is smaller than it's key size. Therefore please always us a [hybrid encryption scheme](https://en.wikipedia.org/wiki/Hybrid_cryptosystem) like RSA+AES. – Robert Oct 18 '21 at 08:08

1 Answers1

0

The issue with the source code is that the variable digest is used for decryption:

byte[] rawDecryptedData = pivSession.Decrypt(0x9D, digest);

Instead, the variable encryptedData should be used for decryption, like this:

byte[] rawDecryptedData = pivSession.Decrypt(0x9D, encryptedData);

The reason why the encrypted data changes is that it is padded with random data according to RSAEncryptionPadding.OaepSHA256 for each encryption operation. The RSA public and private keys at the YubiKey PIV are static and do not change. The data is encrypted with the public RSA key, and this is the key that can be exported and shared. The data is decrypted with the private RSA key, and this key never leaves the YubiKey. It is protected with the PIN-code that must be entered for the decryption operation.

The previous comment to never use RSA to encrypt arbitrary plain text data is correct. The maximum limit for data to be encrypted with RSA is very limited. For example, for RSA 2048 with PKCS #1 the maximum data size becomes 2048/8 - 11 = 256 - 11= 245. Furthermore, the performance of RSA is much slower than AES. Instead, it is recommended combine RSA asymmetric encryption with AES symmetric encryption. This is normally done by generating a session AES key and encrypting it with an RSA encryption key. AES has the advantage of encrypting large data with high performance, while RSA has the advantage of protecting or sharing the AES session key.