我找到了我的问题的答案。我假设如果它是 PGP 规范的一部分,那么它一定是可能的,而不需要太多麻烦。因此,我决定仔细检查解密过程以及整个过程中使用的所有对象。
使用调试器,我枚举了 PgpEncryptedDataList 中的项目,并在单个 PgpPublicKeyEncryptedData 对象中找到了加密它的公钥的密钥 ID。
该对象包含一个名为KeyId 的long 类型的属性。这是我正在寻找与存储在应用程序中的键匹配的值。
下面的sn-p只是我用来达到KeyId属性的一个例子:
using (var inputFile = File.OpenRead(@"E:\Staging\6114d23c-2595abef\testfile.txt.gpg"))
using (var decoderStream = PgpUtilities.GetDecoderStream(inputFile))
{
var objectFactory = new PgpObjectFactory(decoderStream);
var encryptedList = (PgpEncryptedDataList)objectFactory.NextPgpObject();
foreach (var encryptedData in encryptedList.GetEncryptedDataObjects().Cast<PgpPublicKeyEncryptedData>())
{
var keyId = encryptedData.KeyId.ToString("X");
Console.WriteLine($"Encryption Key ID: {keyId}");
}
}
在第一次枚举之后设置断点,您可以检查encryptedData 变量并观察到类似于:
所以,经过一番挣扎,其实很简单。在解密过程中访问KeyId 非常简单,您可以自动获取正确的私钥进行解密。
为了完整起见,对于 PGP,通常会为不止一个收件人加密文件。在这种情况下,您将看到多个加密数据对象。这并不意味着数据被加密两次。只有会话密钥。会话密钥被加密 N 次,其中 N 是接收者的数量。这允许每个接收者能够解密 一个 会话密钥,然后继续解密数据。
参考下图显示两个对象,正如您所料,两个KeyId 属性:)
这个 sn-p 来自 PgpDecrypt.cs,它已经查看了加密对象,并根据作为参数传入的 PgpSecretKeyRingBundle 检查密钥标识符:
foreach (PgpPublicKeyEncryptedData pked in encryptedDataList.GetEncryptedDataObjects())
{
privateKey = PgpKeyHelper.FindSecretKey(secretKeyRing, pked.KeyId, passPhrase.ToCharArray());
if (privateKey == null)
{
continue;
}
encryptedData = pked;
break;
}
对于任何希望在 PGP、BouncyCastle 和 C# 方面领先的人,请refer to my library,其中包含许多 PGP 函数的汇编。可以更改 PgpDecrypt 类以自动合并此问题中讨论的密钥发现。