ent1711
ent1711

Reputation: 9

PGP Decrypt issue with BouncyCastle.Cryptography 2.0.0 and later

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

Answers (2)

Sumedh
Sumedh

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

Hamdy
Hamdy

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

Related Questions