我开发了一个库,用于对一个或多个收件人执行PGP签名/加密和解密/验证文件。这部分非常有效,可以很好地使用流来处理大型文件。
部分PGP消息交换格式规范(RFC 1991)声明如下:
...
6.7用户ID数据包
目的。用户ID包标识用户并与a相关联 公钥或私钥。
定义。用户ID包是以下的串联 字段:
(a)数据包结构字段(2个字节);
(b)用户ID字符串。
用户ID字符串可以是任何可打印的ASCII字符串。 但是,由于此数据包的目的是唯一地标识一个 个人,通常的做法是用户ID字符串组成 用户的名称后跟该用户的电子邮件地址 后者用尖括号括起来。
...
我正在创建的应用程序需要尝试识别用于自动解密文件的适当密钥,以便尽可能少地进行用户干预。如果无法识别密钥(例如,如果隐藏了收件人),则应用程序将提示选择正确的密钥。我试图尽可能地简化它。
RFC建议数据包不是加密数据的一部分,这是有意义的。 PGP可以轻松尝试识别加密数据的人员。当您使用Kleopatra尝试解密文件时,如果将相关密钥添加到其密钥数据库中,则这一点很明显。在这种情况下,它将提示密码保护密钥。
我的具体问题是:
如何使用C#BouncyCastle库来读取加密数据的目标收件人?换句话说,哪个私钥用于解密?
我尝试使用Bouncy Castle GitHub repository查找示例,但无法看到任何显示此特定问题的示例。我也看了很多谷歌搜索结果这个问题无济于事。
答案 0 :(得分:0)
我找到了问题的答案。我认为如果它是PGP规范的一部分那么它必须是没有太多麻烦的。因此,我决定仔细审查解密过程以及整个过程中使用的所有对象。
使用调试器我枚举了PgpEncryptedDataList
中的项目,并找到了在单个PgpPublicKeyEncryptedData
对象中加密它的公钥的密钥ID。
该对象包含名为long
的{{1}}类型的属性。这是我想要与应用程序中存储的密钥匹配的值。
以下代码段只是我用来访问KeyId
属性的示例:
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
非常简单,您可以自动获取正确的私钥来进行解密。
为了完整起见,PGP通常会为多个收件人加密文件。在这种情况下,您将看到多个加密数据对象。这并不意味着数据被加密两次。只有会话密钥。会话密钥被加密N次,其中N是接收者的数量。这允许每个收件人能够解密一个会话密钥,然后继续解密数据。
请参阅下图显示两个对象,如您所料,两个KeyId
属性:)
此代码段来自已查看加密对象的PgpDecrypt.cs,并根据作为参数传入的KeyId
检查密钥标识符:
PgpSecretKeyRingBundle
对于任何希望拥有PGP,BouncyCastle和C#的人,请refer to my library包含许多PGP功能的汇编。可以更改foreach (PgpPublicKeyEncryptedData pked in encryptedDataList.GetEncryptedDataObjects())
{
privateKey = PgpKeyHelper.FindSecretKey(secretKeyRing, pked.KeyId, passPhrase.ToCharArray());
if (privateKey == null)
{
continue;
}
encryptedData = pked;
break;
}
类以自动合并密钥发现,如本问题中所述。