使用BouncyCastle C#库

时间:2017-12-03 12:31:41

标签: c# bouncycastle

我开发了一个库,用于对一个或多个收件人执行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查找示例,但无法看到任何显示此特定问题的示例。我也看了很多谷歌搜索结果这个问题无济于事。

1 个答案:

答案 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}"); } } 变量并观察类似于:

enter image description here

所以,经过所有的斗争,它实际上非常简单。在解密过程中访问encryptedData非常简单,您可以自动获取正确的私钥来进行解密。

为了完整起见,PGP通常会为多个收件人加密文件。在这种情况下,您将看到多个加密数据对象。这并不意味着数据被加密两次。只有会话密钥。会话密钥被加密N次,其中N是接收者的数量。这允许每个收件人能够解密一个会话密钥,然后继续解密数据。

请参阅下图显示两个对象,如您所料,两个KeyId属性:)

enter image description here

此代码段来自已查看加密对象的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; } 类以自动合并密钥发现,如本问题中所述。