【问题标题】:PgP Encryption and Decryption using BouncyCastle c#使用 BouncyCastle c# 进行 PgP 加密和解密
【发布时间】:2011-10-22 16:41:13
【问题描述】:

我看过很多帖子,学习了很多教程,但似乎没有一个有效。有时,它们会引用一些未找到的类。能否指点一个地方,在那里我可以获得一个简单的教程,展示如何加密和解密文件。

我是 Pgp 的新手,欢迎任何帮助。

【问题讨论】:

    标签: c# encryption bouncycastle pgp


    【解决方案1】:

    我知道这个问题已有多年历史,但在 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);
            }
        }
    }
    

    【讨论】:

    • 不完全是我需要的,但足够接近我的需要。为我节省了大量的工作。 Bouncy Castle 仍然是我在 .NET 中为 PGP 找到的最好的免费解决方案,并且也击败了一些付费解决方案。
    • 你知道如何解决“加密消息包含签名消息 - 不是文字数据”。错误?
    • @MokhAkh,如果您查看我的代码,您会发现当消息是 PgpOnePassSignatureList 而不是预期的 PgpLiteralData 时会引发此错误。我不知道你的确切情况,但我猜如果你开始在 SO 或 Google 中搜索 PgpOnePassSignatureList,你就会开始弄清楚。
    • 阅读下一篇plainFact.NextPgpObject();解决我的问题,非常感谢
    • @MokhAkh,您知道是什么导致第一条消息的类型为 PgpOnePassSignatureList 而不是 PgpLiteralData?是因为数据被加密的特殊方式吗?我只发现一条 Ruby 论坛消息在谈论相同的问题并提出相同的解决方案。 github.com/sgonyea/jruby-pgp/issues/2但内因是什么?
    【解决方案2】:

    怎么样:

    PartialInputStream during Bouncycastle PGP decryption

    此外,zip 包含此处的示例:

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

    希望这会有所帮助。如果您仍然卡住,请发布有关编译器抱怨的类的更多详细信息,社区将会查看。

    【讨论】:

    • 嗨。我想我会写一个完整的解决方案并发布我的代码,其中包含我可能遇到的错误。将检查解密代码。
    • link 找到了一个示例。它既有加密又有解密。谢谢
    【解决方案3】:

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

    它非常干净且易于使用。多个示例可用here

    【讨论】:

      【解决方案4】:

      现在,在 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!");
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-10
        • 1970-01-01
        • 1970-01-01
        • 2021-03-22
        • 2012-06-02
        • 2012-04-29
        相关资源
        最近更新 更多