使用 BouncyCastle c# 进行 PgP 加密和解密

发布于 2024-11-28 19:16:58 字数 113 浏览 2 评论 0原文

我看过很多帖子,遵循了很多教程,但似乎都不起作用。有时,他们会引用一些未找到的类。我可以指出一个地方,在那里我可以获得一个简单的教程,展示如何加密和解密文件。

我对 Pgp 很陌生,欢迎任何帮助。

I've seen a number of posts, followed a number of tutorials but none seems to work. Sometimes, they make reference to some classes which are not found. Can I be pointed to a place where I can get a simple tutorial showing how to encrypt and decrypt a file.

I'm very new to Pgp and any assistance is welcomed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

錯遇了你 2024-12-05 19:16:58

我知道这个问题已经有很多年了,但在 Google 中与使用 Bouncy Castle 进行 PGP 解密相关的搜索中,它仍然排名第一或第二。由于似乎很难找到一个完整、简洁的示例,我想在这里分享我的解密 PGP 文件的工作解决方案。这只是源文件中包含的 Bouncy Castle 示例的修改版本。

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

namespace PGPDecrypt
{
    class Program
    {
        static void Main(string[] args)
        {
            DecryptFile(
                @"path_to_encrypted_file.pgp",
                @"path_to_secret_key.asc",
                "your_password_here".ToCharArray(), 
                "output.txt"
            );
        }

        private static void DecryptFile(
            string inputFileName,
            string keyFileName,
            char[] passwd,
            string defaultFileName)
        {
            using (Stream input = File.OpenRead(inputFileName),
                   keyIn = File.OpenRead(keyFileName))
            {
                DecryptFile(input, keyIn, passwd, defaultFileName);
            }
        }

        private static void DecryptFile(
            Stream inputStream,
            Stream keyIn,
            char[] passwd,
            string defaultFileName)
        {
            inputStream = PgpUtilities.GetDecoderStream(inputStream);

            try
            {
                PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
                PgpEncryptedDataList enc;

                PgpObject o = pgpF.NextPgpObject();
                //
                // the first object might be a PGP marker packet.
                //
                if (o is PgpEncryptedDataList)
                {
                    enc = (PgpEncryptedDataList)o;
                }
                else
                {
                    enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
                }

                //
                // find the secret key
                //
                PgpPrivateKey sKey = null;
                PgpPublicKeyEncryptedData pbe = null;
                PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(
                    PgpUtilities.GetDecoderStream(keyIn));

                foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
                {
                    sKey = FindSecretKey(pgpSec, pked.KeyId, passwd);

                    if (sKey != null)
                    {
                        pbe = pked;
                        break;
                    }
                }

                if (sKey == null)
                {
                    throw new ArgumentException("secret key for message not found.");
                }

                Stream clear = pbe.GetDataStream(sKey);

                PgpObjectFactory plainFact = new PgpObjectFactory(clear);

                PgpObject message = plainFact.NextPgpObject();

                if (message is PgpCompressedData)
                {
                    PgpCompressedData cData = (PgpCompressedData)message;
                    PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());

                    message = pgpFact.NextPgpObject();
                }

                if (message is PgpLiteralData)
                {
                    PgpLiteralData ld = (PgpLiteralData)message;

                    string outFileName = ld.FileName;
                    if (outFileName.Length == 0)
                    {
                        outFileName = defaultFileName;
                    }

                    Stream fOut = File.Create(outFileName);
                    Stream unc = ld.GetInputStream();
                    Streams.PipeAll(unc, fOut);
                    fOut.Close();
                }
                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.");
                }

                if (pbe.IsIntegrityProtected())
                {
                    if (!pbe.Verify())
                    {
                        Console.Error.WriteLine("message failed integrity check");
                    }
                    else
                    {
                        Console.Error.WriteLine("message integrity check passed");
                    }
                }
                else
                {
                    Console.Error.WriteLine("no message integrity check");
                }
            }
            catch (PgpException e)
            {
                Console.Error.WriteLine(e);

                Exception underlyingException = e.InnerException;
                if (underlyingException != null)
                {
                    Console.Error.WriteLine(underlyingException.Message);
                    Console.Error.WriteLine(underlyingException.StackTrace);
                }
            }
        }

        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 know this question is years old but it is still #1 or #2 in Google for searches related to PGP Decryption using Bouncy Castle. Since it seems hard to find a complete, succinct example I wanted to share my working solution here for decrypting a PGP file. This is simply a modified version of the Bouncy Castle example included with their source files.

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

namespace PGPDecrypt
{
    class Program
    {
        static void Main(string[] args)
        {
            DecryptFile(
                @"path_to_encrypted_file.pgp",
                @"path_to_secret_key.asc",
                "your_password_here".ToCharArray(), 
                "output.txt"
            );
        }

        private static void DecryptFile(
            string inputFileName,
            string keyFileName,
            char[] passwd,
            string defaultFileName)
        {
            using (Stream input = File.OpenRead(inputFileName),
                   keyIn = File.OpenRead(keyFileName))
            {
                DecryptFile(input, keyIn, passwd, defaultFileName);
            }
        }

        private static void DecryptFile(
            Stream inputStream,
            Stream keyIn,
            char[] passwd,
            string defaultFileName)
        {
            inputStream = PgpUtilities.GetDecoderStream(inputStream);

            try
            {
                PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
                PgpEncryptedDataList enc;

                PgpObject o = pgpF.NextPgpObject();
                //
                // the first object might be a PGP marker packet.
                //
                if (o is PgpEncryptedDataList)
                {
                    enc = (PgpEncryptedDataList)o;
                }
                else
                {
                    enc = (PgpEncryptedDataList)pgpF.NextPgpObject();
                }

                //
                // find the secret key
                //
                PgpPrivateKey sKey = null;
                PgpPublicKeyEncryptedData pbe = null;
                PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(
                    PgpUtilities.GetDecoderStream(keyIn));

                foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
                {
                    sKey = FindSecretKey(pgpSec, pked.KeyId, passwd);

                    if (sKey != null)
                    {
                        pbe = pked;
                        break;
                    }
                }

                if (sKey == null)
                {
                    throw new ArgumentException("secret key for message not found.");
                }

                Stream clear = pbe.GetDataStream(sKey);

                PgpObjectFactory plainFact = new PgpObjectFactory(clear);

                PgpObject message = plainFact.NextPgpObject();

                if (message is PgpCompressedData)
                {
                    PgpCompressedData cData = (PgpCompressedData)message;
                    PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());

                    message = pgpFact.NextPgpObject();
                }

                if (message is PgpLiteralData)
                {
                    PgpLiteralData ld = (PgpLiteralData)message;

                    string outFileName = ld.FileName;
                    if (outFileName.Length == 0)
                    {
                        outFileName = defaultFileName;
                    }

                    Stream fOut = File.Create(outFileName);
                    Stream unc = ld.GetInputStream();
                    Streams.PipeAll(unc, fOut);
                    fOut.Close();
                }
                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.");
                }

                if (pbe.IsIntegrityProtected())
                {
                    if (!pbe.Verify())
                    {
                        Console.Error.WriteLine("message failed integrity check");
                    }
                    else
                    {
                        Console.Error.WriteLine("message integrity check passed");
                    }
                }
                else
                {
                    Console.Error.WriteLine("no message integrity check");
                }
            }
            catch (PgpException e)
            {
                Console.Error.WriteLine(e);

                Exception underlyingException = e.InnerException;
                if (underlyingException != null)
                {
                    Console.Error.WriteLine(underlyingException.Message);
                    Console.Error.WriteLine(underlyingException.StackTrace);
                }
            }
        }

        private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyID, char[] pass)
        {
            PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyID);

            if (pgpSecKey == null)
            {
                return null;
            }

            return pgpSecKey.ExtractPrivateKey(pass);
        }
    }
}
离线来电— 2024-12-05 19:16:58

我使用了 PgpCore 包,它是 Portable.BouncyCastle 的包装器。

它非常干净且易于使用。 此处提供了多个示例。

I have used PgpCore package which is a wrapper around Portable.BouncyCastle.

It is very clean and simple to use. Multiple examples available here.

日记撕了你也走了 2024-12-05 19:16:58

这是怎么回事:

Bouncycastle PGP 解密期间的 PartialInputStream

另外,该 zip 包含示例:

http://www.bouncycastle.org/csharp/

希望如此有帮助。如果您仍然遇到困难,请发布有关编译器抱怨的类的更多详细信息,社区将会查看。

How's this:

PartialInputStream during Bouncycastle PGP decryption

Also, the zip contains examples here:

http://www.bouncycastle.org/csharp/

Hope this helps. If you're still stuck, post some more detail about what classes the compiler is complaining about and the community will take a look.

何时共饮酒 2024-12-05 19:16:58

现在,到了 2021 年,Nikhil 的答案可能是最好的,因为它抽象出了直接与 BouncyCastle 合作的需要。快去给他投一票吧。

如果您出于某种原因想直接使用 BouncyCastle,我有 Dan 的答案的现代实现以及他正在使用的示例,它直接在 NET5 中使用 BouncyCastle。看一下:

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

安装的是 Nuget Package Portable.BouncyCastle 1.8.10。

public class EncryptionService 
{
    public static void EncryptPGPFile(FileInfo inFile, FileInfo keyFile, FileInfo outFile, bool withIntegrityCheck = false, bool withArmor = false)
    {
        PgpPublicKeyRingBundle keyRing = null;
        using (var keyStream = keyFile.OpenRead())
        {
            keyRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
        }

        var publicKey = keyRing.GetKeyRings()
            .Cast<PgpPublicKeyRing>()
            .FirstOrDefault()
            ?.GetPublicKeys()
            .Cast<PgpPublicKey>()
            .FirstOrDefault(x => x.IsEncryptionKey);

        using var outFileStream = outFile.Open(FileMode.Create);
        using var armoredStream = new ArmoredOutputStream(outFileStream);
        Stream outStream = withArmor ? armoredStream : outFileStream;

        byte[] compressedBytes;

        var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);

        using (var byteStream = new MemoryStream())
        {
            // Annoyingly, this is necessary. The compressorStream needs to be closed before the byteStream is read from, otherwise
            // data will be left in the buffer and not written to the byteStream. It would be nice if compressorStream exposed a "Flush"
            // method. - AJS
            using (var compressorStream = compressor.Open(byteStream))
            {
                PgpUtilities.WriteFileToLiteralData(compressorStream, PgpLiteralData.Binary, inFile);
            }
            compressedBytes = byteStream.ToArray();
        };

        var encrypter = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
        encrypter.AddMethod(publicKey);

        using var finalOutputStream = encrypter.Open(outStream, compressedBytes.Length);
        finalOutputStream.Write(compressedBytes, 0, compressedBytes.Length);
    }

    public static void DecryptPGPFile(FileInfo inFile, FileInfo keyFile, string password, FileInfo outFile)
    {
        using var inputFile = inFile.OpenRead();
        using var input = PgpUtilities.GetDecoderStream(inputFile);

        var pgpFactory = new PgpObjectFactory(input);

        var firstObject = pgpFactory.NextPgpObject();
        if (firstObject is not PgpEncryptedDataList)
        {
            firstObject = pgpFactory.NextPgpObject();
        }

        PgpPrivateKey keyToUse = null;
        PgpSecretKeyRingBundle keyRing = null;

        using (var keyStream = keyFile.OpenRead())
        {
            keyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
        }

        var encryptedData = ((PgpEncryptedDataList)firstObject).GetEncryptedDataObjects()
            .Cast<PgpPublicKeyEncryptedData>()
            .FirstOrDefault(x =>
            {
                var key = keyRing.GetSecretKey(x.KeyId);
                if (key != null)
                {
                    keyToUse = key.ExtractPrivateKey(password.ToCharArray());
                    return true;
                }
                return false;
            });

        if (keyToUse == null)
        {
            throw new PgpException("Cannot find secret key for message.");
        }

        Stream clearText = encryptedData.GetDataStream(keyToUse);
        PgpObject message = new PgpObjectFactory(clearText).NextPgpObject();

        if (message is PgpCompressedData data)
        {
            message = new PgpObjectFactory(inputStream: data.GetDataStream()).NextPgpObject();
        }

        if (message is PgpLiteralData literalData)
        {
            using var outputFileStream = outFile.Open(FileMode.Create);
            Streams.PipeAll(literalData.GetInputStream(), outputFileStream);
        }
        else
        {
            throw new PgpException("message is not encoded correctly.");
        }

        if (encryptedData.IsIntegrityProtected() && !encryptedData.Verify())
        {
            throw new Exception("message failed integrity check!");
        }
    }
}

Now, in 2021, Nikhil's answer is probably best, since it abstracts out the need for working with BouncyCastle directly. Go give him an upvote.

If you want to work with BouncyCastle directly for some reason, I've got a modern implementation of Dan's answer, and the examples he's working from, that uses BouncyCastle directly in NET5. Take a look:

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

Installed is Nuget Package Portable.BouncyCastle 1.8.10.

public class EncryptionService 
{
    public static void EncryptPGPFile(FileInfo inFile, FileInfo keyFile, FileInfo outFile, bool withIntegrityCheck = false, bool withArmor = false)
    {
        PgpPublicKeyRingBundle keyRing = null;
        using (var keyStream = keyFile.OpenRead())
        {
            keyRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
        }

        var publicKey = keyRing.GetKeyRings()
            .Cast<PgpPublicKeyRing>()
            .FirstOrDefault()
            ?.GetPublicKeys()
            .Cast<PgpPublicKey>()
            .FirstOrDefault(x => x.IsEncryptionKey);

        using var outFileStream = outFile.Open(FileMode.Create);
        using var armoredStream = new ArmoredOutputStream(outFileStream);
        Stream outStream = withArmor ? armoredStream : outFileStream;

        byte[] compressedBytes;

        var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);

        using (var byteStream = new MemoryStream())
        {
            // Annoyingly, this is necessary. The compressorStream needs to be closed before the byteStream is read from, otherwise
            // data will be left in the buffer and not written to the byteStream. It would be nice if compressorStream exposed a "Flush"
            // method. - AJS
            using (var compressorStream = compressor.Open(byteStream))
            {
                PgpUtilities.WriteFileToLiteralData(compressorStream, PgpLiteralData.Binary, inFile);
            }
            compressedBytes = byteStream.ToArray();
        };

        var encrypter = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
        encrypter.AddMethod(publicKey);

        using var finalOutputStream = encrypter.Open(outStream, compressedBytes.Length);
        finalOutputStream.Write(compressedBytes, 0, compressedBytes.Length);
    }

    public static void DecryptPGPFile(FileInfo inFile, FileInfo keyFile, string password, FileInfo outFile)
    {
        using var inputFile = inFile.OpenRead();
        using var input = PgpUtilities.GetDecoderStream(inputFile);

        var pgpFactory = new PgpObjectFactory(input);

        var firstObject = pgpFactory.NextPgpObject();
        if (firstObject is not PgpEncryptedDataList)
        {
            firstObject = pgpFactory.NextPgpObject();
        }

        PgpPrivateKey keyToUse = null;
        PgpSecretKeyRingBundle keyRing = null;

        using (var keyStream = keyFile.OpenRead())
        {
            keyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyStream));
        }

        var encryptedData = ((PgpEncryptedDataList)firstObject).GetEncryptedDataObjects()
            .Cast<PgpPublicKeyEncryptedData>()
            .FirstOrDefault(x =>
            {
                var key = keyRing.GetSecretKey(x.KeyId);
                if (key != null)
                {
                    keyToUse = key.ExtractPrivateKey(password.ToCharArray());
                    return true;
                }
                return false;
            });

        if (keyToUse == null)
        {
            throw new PgpException("Cannot find secret key for message.");
        }

        Stream clearText = encryptedData.GetDataStream(keyToUse);
        PgpObject message = new PgpObjectFactory(clearText).NextPgpObject();

        if (message is PgpCompressedData data)
        {
            message = new PgpObjectFactory(inputStream: data.GetDataStream()).NextPgpObject();
        }

        if (message is PgpLiteralData literalData)
        {
            using var outputFileStream = outFile.Open(FileMode.Create);
            Streams.PipeAll(literalData.GetInputStream(), outputFileStream);
        }
        else
        {
            throw new PgpException("message is not encoded correctly.");
        }

        if (encryptedData.IsIntegrityProtected() && !encryptedData.Verify())
        {
            throw new Exception("message failed integrity check!");
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文