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
相关产品推荐
相关产品推荐

