c# - 如何使用 yubikey SDK for .NET 解密文本?
问题描述
我正在尝试使用 yubikey 在我的应用程序中加密/解密字符串。到目前为止,我写了一些用于测试目的的方法。我的代码基于我在官方存储库中找到的示例:Yubico.YubiKey/examples/PivSampleCode/。
但是,我无法解密任何内容。我得到一个 InvalidOperationException: Referenced data or reference data not found。
我还注意到,每次我运行该方法时,加密字符串都会发生变化,对吗?我需要能够使用内部私钥加密并共享公钥。但是,如果每次更改密钥对对我来说都是一个大问题。你们有什么建议吗?
这是我正在使用的代码,都加密但不解密:
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());
}
}
}
各位,有什么建议吗?
提前致谢!
解决方案
源代码的问题是变量摘要用于解密:
byte[] rawDecryptedData = pivSession.Decrypt(0x9D, 摘要);
相反,变量 encryptedData 应该用于解密,如下所示:
byte[] rawDecryptedData = pivSession.Decrypt(0x9D, encryptedData);
加密数据发生变化的原因是每次加密操作都按照RSAEncryptionPadding.OaepSHA256填充随机数据。YubiKey PIV 上的 RSA 公钥和私钥是静态的,不会更改。数据使用公共 RSA 密钥加密,这是可以导出和共享的密钥。数据用私有 RSA 密钥解密,这个密钥永远不会离开 YubiKey。它受解密操作必须输入的 PIN 码保护。
先前关于从不使用 RSA 加密任意纯文本数据的评论是正确的。使用 RSA 加密的数据的最大限制非常有限。例如,对于 PKCS #1 的 RSA 2048,最大数据大小变为 2048/8 - 11 = 256 - 11= 245。此外,RSA 的性能比 AES 慢得多。相反,建议将 RSA 非对称加密与 AES 对称加密结合使用。这通常通过生成会话 AES 密钥并使用 RSA 加密密钥对其进行加密来完成。AES 具有以高性能加密大数据的优势,而 RSA 具有保护或共享 AES 会话密钥的优势。
推荐阅读
- compilation - “内存中二进制内存映像”是什么意思?
- windows - 无法从 echo $PATH 中删除路径
- c++ - 为什么在 Main 中正常运行的代码在函数内部运行不正确?(C++)
- c++ - C++ 中的适配器模式返回分段错误
- mongodb - WSo2 tls 连接到 mongodb 数据服务
- java - slf4j 找不到 log4j2 jar
- vue.js - 如何在vue组件的样式标签中添加if
- discord.js - 我正在尝试发出命令来显示不和谐机器人在多少台服务器上,但得到错误“消息未定义”
- apache-kafka - Kafka Manager - 需要跟踪使用它的用户以确保安全
- android - 形状可绘制是视图之间的共享对象吗