You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BouncyCastle.Cryptography 2.0+版本PGP解密流已释放异常求助

PGP解密适配BouncyCastle.Cryptography 2.0+版本修复方案

问题概述

原生产环境基于BouncyCastle.Cryptography 1.9.0版本,使用Stack Overflow上M.Babcock的PGP加解密类运行正常。升级至2.0.0+版本(当前最新2.2.1)后,解密方法中出现System.ObjectDisposedException: 'Cannot access a closed stream. Object name: 'ZLibStream'.'异常,触发异常的代码片段如下:

if (message is PgpCompressedData)
{
    PgpCompressedData cData = (PgpCompressedData)message;
    PgpObjectFactory of = null;

    using (Stream compDataIn = cData.GetDataStream())
    {
        of = new PgpObjectFactory(compDataIn);
    }

    message = of.NextPgpObject();
   //...
}

用户参考第三方代码修改后仍存在相同问题,需调整原有逻辑适配新版本。

核心原因与修复方案

异常本质是**using块自动关闭了compDataIn流**,而BouncyCastle 2.0+版本中PgpObjectFactory不会缓存流数据,后续调用of.NextPgpObject()时流已被释放。需调整流的生命周期管理,确保读取PGP对象时流始终处于打开状态。

修改后的完整解密代码如下:

using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Utilities.IO;
using System.IO;

namespace TestingPGP
{
    static class Program
    {
        static void Main(string[] args)
        {
            string inputfile = @"d:\Testing\EncryptedFile.gpg";
            string outputFile = @"d:\Testing\DecryptedFile.gpg";
            string privateKeyFile = @"d:\Testing\MyPrivateKey.asc";
            string passPhrase = "hereismypassphrase";
            PgpPublicKeyEncryptedData pbe = null;
            PgpObjectFactory pgpF = null;
            PgpEncryptedDataList enc = null;
            PgpObject o = null;
            PgpPrivateKey privateKey = null;
            PgpSecretKeyRingBundle pgpSec = null;

            using (Stream inputStream = File.OpenRead(inputfile))
            using (Stream privateKeyStream = File.OpenRead(privateKeyFile))
            {
                pgpF = new PgpObjectFactory(PgpUtilities.GetDecoderStream(inputStream));
                pgpSec = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream));

                o = pgpF.NextPgpObject();

                // 处理PGP标记包
                enc = o is PgpEncryptedDataList 
                    ? (PgpEncryptedDataList)o 
                    : (PgpEncryptedDataList)pgpF.NextPgpObject();

                // 查找对应私钥
                foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
                {
                    privateKey = FindSecretKey(pgpSec, pked.KeyId, passPhrase.ToCharArray());
                    if (privateKey != null)
                    {
                        pbe = pked;
                        break;
                    }
                }

                if (privateKey == null)
                    throw new ArgumentException("未找到消息对应的私钥。");

                // 直接使用解密流创建PGP对象工厂,无需提前复制到内存流
                using (Stream decryptedStream = pbe.GetDataStream(privateKey))
                using (PgpObjectFactory plainFact = new PgpObjectFactory(decryptedStream))
                {
                    PgpObject message = plainFact.NextPgpObject();

                    // 处理压缩数据
                    if (message is PgpCompressedData cData)
                    {
                        // 保持压缩流打开,直到读取完所有PGP对象
                        using (Stream compDataIn = cData.GetDataStream())
                        using (PgpObjectFactory compressedFact = new PgpObjectFactory(compDataIn))
                        {
                            message = compressedFact.NextPgpObject();

                            // 跳过签名列表(如果存在)
                            if (message is PgpOnePassSignatureList)
                                message = compressedFact.NextPgpObject();

                            // 写入明文文件
                            if (message is PgpLiteralData literalData)
                            {
                                using (Stream output = File.Create(outputFile))
                                using (Stream unc = literalData.GetInputStream())
                                {
                                    Streams.PipeAll(unc, output);
                                }
                            }
                        }
                    }
                    // 处理未压缩的明文数据
                    else if (message is PgpLiteralData literalData)
                    {
                        using (Stream output = File.Create(outputFile))
                        using (Stream unc = literalData.GetInputStream())
                        {
                            Streams.PipeAll(unc, output);
                        }
                    }
                }
            }
        }

        private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyId, char[] pass)
        {
            PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyId);
            return pgpSecKey?.ExtractPrivateKey(pass);
        }
    }
}

关键修改点

  • 流生命周期对齐:将PgpObjectFactory的创建和使用放在对应流的using块内部,确保流在读取过程中始终保持打开状态。
  • 移除冗余内存复制:直接使用解密后的流创建PgpObjectFactory,避免内存浪费和流生命周期冲突。
  • 简化分支逻辑:合并重复的文件写入代码,提升代码可读性。
  • 空值安全处理:使用?.运算符简化私钥提取的空值判断。

内容的提问来源于stack exchange,提问作者ent1711

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:54:55