【问题标题】:Identify the recipient(s) KeyId from PGP encrypted data using the BouncyCastle C# library使用 BouncyCastle C# 库从 PGP 加密数据中识别收件人 KeyId
【发布时间】:2017-12-03 12:31:41
【问题描述】:

我开发了一个库来针对一个或多个收件人执行文件的 PGP 签名/加密和解密/验证。这部分效果很好,可以很好地使用流处理大文件。

PGP 消息交换格式规范 (RFC 1991) 的一部分规定如下:

...

6.7 用户ID包

目的。用户 ID 数据包标识用户并与 公钥或私钥。

定义。用户 ID 数据包是以下内容的串联 字段:

(a) 数据包结构域(2字节);

(b) 用户 ID 字符串。

用户 ID 字符串可以是任何可打印的 ASCII 字符串。 但是,由于此数据包的目的是唯一标识一个 个人,通常的做法是用户 ID 字符串由 用户名后跟该用户的电子邮件地址, 后者用尖括号括起来。

...

我正在创建的应用程序将需要尝试识别用于自动解密文件的适当密钥,以便我尽可能少地进行用户干预。如果无法识别密钥(例如,如果收件人被隐藏),应用程序将提示选择正确的密钥。我正在努力使其尽可能精简。

RFC 建议数据包不是有意义的加密数据的一部分。 PGP 使尝试和识别谁加密了数据变得容易。当您尝试使用 Kleopatra 解密文件并将相关密钥添加到其密钥数据库中时,这一点很明显。在这种情况下,它会提示输入保护密钥的密码。

我的具体问题是:

如何使用 C# BouncyCastle 库来读取加密数据的目标收件人?换句话说,使用哪个私钥来解密?

我尝试使用Bouncy Castle GitHub repository 查找示例,但看不到任何证明此特定问题的示例。我还针对这个问题查看了尽可能多的谷歌搜索结果。

【问题讨论】:

    标签: c# bouncycastle


    【解决方案1】:

    我找到了我的问题的答案。我假设如果它是 PGP 规范的一部分,那么它一定是可能的,而不需要太多麻烦。因此,我决定仔细检查解密过程以及​​整个过程中使用的所有对象。

    使用调试器,我枚举了 PgpEncryptedDataList 中的项目,并在单个 PgpPublicKeyEncryptedData 对象中找到了加密它的公钥的密钥 ID。

    该对象包含一个名为KeyIdlong 类型的属性。这是我正在寻找与存储在应用程序中的键匹配的值。

    下面的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 类以自动合并此问题中讨论的密钥发现。

    【讨论】:

      猜你喜欢
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多