Reputation: 9
I do not have much experience with PGP as a programmer. For work, I have been using, un-modified in anyway, the classes found in the top answer for https://stackoverflow.com/questions/10209291/pgp-encrypt-and-decrypt" by M.Babcock for many years whenever I have a need to decrypt or encrypt a file. Up to this point, I have regularly updated to the latest BouncyCastle.Cryptography version. Currently, my production code is on BouncyCastle.Cryptography version 1.9.0.
When I try to upgrade BouncyCastle.Cryptography to version 2.0.0 or later in my project (console application, .net 6.0), this code will error out in the class PGPEncryptDecrypt
of that solution, Decrypt
method using a Stream input. Specifically, it errors out at the last line of the code snippet below
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
PgpObjectFactory of = null;
using (Stream compDataIn = cData.GetDataStream())
{
of = new PgpObjectFactory(compDataIn);
}
message = of.NextPgpObject();
//...
}
The exception occurs on the line message = of.NextPgpObject():
"System.ObjectDisposedException: 'Cannot access a closed stream. Object name: 'ZLibStream'."
When I roll back to version 1.9.0 the code is working as expected again. I have searched for examples of C# code using the 2.0.0 or later version but have not found any. In my search, I did go to bouncycastle.org and saw on the release notes that version 2.0.0 and later does contain breaking changes. However, I'm not knowledgeable enough to know how to modify the class PGPEncrtypDecrypt so that it works with version 2.0.0 and later.
What needs to be changed in the classes given in the top answer for https://stackoverflow.com/questions/10209291/pgp-encrypt-and-decrypt" so that it works with BouncyCastle.Cryptography 2.0.0. I would like to use the latest, which at the time of this post is 2.2.1.
Incorporating code from Hamdy's response, my current test code is
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.Utilities.Zlib;
namespace TestingPGP
{
static class Program
{
string inputfile = @"d:\Testing\EncryptedFile.gpg";
string outputFile = @"d:\Testing\DecryptedFile.gpg";
string privateKeyFile = @"d:\Testing\MyPrivateKey.asc";
string passPhrase = "hereismypassphrase";
byte[] plainBytes = null;
PgpPublicKeyEncryptedData pbe = null;
PgpObjectFactory pgpF = null;
PgpEncryptedDataList enc = null;
PgpObject o = null;
PgpPrivateKey privateKey = null;
PgpSecretKeyRingBundle pgpSec = null;
PgpObjectFactory plainFact = null;
using (Stream inputStream = File.OpenRead(inputfile))
{
using (Stream privateKeyStream = File.OpenRead(privateKeyFile))
{
pgpF = new PgpObjectFactory(PgpUtilities.GetDecoderStream(inputStream));
// find secret key
pgpSec = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream));
if (pgpF != null)
{
o = pgpF.NextPgpObject();
}
// the first object might be a PGP marker packet.
if (o is PgpEncryptedDataList)
{
enc = (PgpEncryptedDataList)o;
}
else
{
enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
}
// decrypt
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("Secret key for message not found.");
}
else
{
using (Stream stream = pbe.GetDataStream(privateKey))
{
// To avoid Cannot Access a closed stream
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
plainBytes = memoryStream.ToArray();
}
}
//plainFact = new PgpObjectFactory(stream);
plainFact = new PgpObjectFactory(plainBytes);
// how to uncompress plainFact and save in file here
PgpObject message = plainFact.NextPgpObject();
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
PgpObjectFactory of = null;
using (Stream compDataIn = cData.GetDataStream())
{
of = new PgpObjectFactory(compDataIn);
}
//The next line will generate an exception
message = of.NextPgpObject();
if (message is PgpOnePassSignatureList)
{
message = of.NextPgpObject();
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
using (Stream output = File.Create(outputFile))
{
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
}
else
{
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
using (Stream output = File.Create(outputFile))
{
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
}
}
}
}
}
}
}
private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyId, char[] pass)
{
PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyId);
if (pgpSecKey == null)
{
return null;
}
return pgpSecKey.ExtractPrivateKey(pass);
}
}
I do not know how to progress from plainfact to a saved text file without causing the exception generated at the line "message = of.NextPgpObject();"
Upvotes: 0
Views: 908
Reputation: 1
I have resolved the above issue on line "message = of.NextPgpObject();" by changing below part for using stream section
PgpObjectFactory plainFact = null;
using (Stream clear = pbe.GetDataStream(sKey))
{
plainFact = new PgpObjectFactory(clear);
PgpObject message = plainFact.NextPgpObject();
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
PgpObjectFactory of = null;
using (Stream compDataIn = cData.GetDataStream())
{
of = new PgpObjectFactory(compDataIn);
message = of.NextPgpObject();
if (message is PgpOnePassSignatureList)
{
message = of.NextPgpObject();
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
using (Stream output = File.Create(outputFile))
{
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
}
else
{
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
using (Stream output = File.Create(outputFile))
{
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
}
}
}
else if (message is PgpLiteralData)
{
PgpLiteralData ld = (PgpLiteralData)message;
string outFileName = ld.FileName;
using (Stream fOut = File.Create(outputFile))
{
Stream unc = ld.GetInputStream();
Streams.PipeAll(unc, fOut);
}
}
else if (message is PgpOnePassSignatureList)
throw new PgpException("Encrypted message contains a signed message - not literal data.");
else
throw new PgpException("Message is not a simple encrypted file - type unknown.");
}
Upvotes: 0
Reputation: 1
I fixed it by avoiding the use of the stream. Just change the stream to byte and use the bytes it worked
byte[] plainBytes = null;
using (Stream stream = pbe.GetDataStream(privateKey))
{
// To avoid Cannot Access a closed stream
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
plainBytes = memoryStream.ToArray();
}
//plainFact = new PgpObjectFactory(stream);
plainFact = new PgpObjectFactory(plainBytes);
}
Upvotes: 0