PGP Encrypt and Decrypt

前端 未结 1 1224
慢半拍i
慢半拍i 2020-12-13 10:31

I am really struggling with this, I need to encrypt and decrypt a string using BouncyCastle in C#. I really did try to do this on my own. I did manage to create my own key

相关标签:
1条回答
  • 2020-12-13 11:28

    It took me a while to find a decent example of using bouncy castle for PGP. This is what I use in production. I'm pretty sure it originated from here.

    using System;
    using System.IO;
    using Org.BouncyCastle.Bcpg;
    using Org.BouncyCastle.Bcpg.OpenPgp;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities.IO;
    
    namespace FileEncryptionTasks.Helpers.PGP
    {
        public static class PGPEncryptDecrypt
        {
            private const int BufferSize = 0x10000; // should always be power of 2
    
            #region Encrypt
    
            /*
             * Encrypt the file.
             */
    
            public static void EncryptFile(string inputFile, string outputFile, string publicKeyFile, bool armor, bool withIntegrityCheck)
            {
                try
                {
                    using (Stream publicKeyStream = File.OpenRead(publicKeyFile))
                    {
                        PgpPublicKey encKey = ReadPublicKey(publicKeyStream);
    
                        using (MemoryStream bOut = new MemoryStream())
                        {
                            PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
                            PgpUtilities.WriteFileToLiteralData(comData.Open(bOut), PgpLiteralData.Binary, new FileInfo(inputFile));
    
                            comData.Close();
                            PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
    
                            cPk.AddMethod(encKey);
                            byte[] bytes = bOut.ToArray();
    
                            using (Stream outputStream = File.Create(outputFile))
                            {
                                if (armor)
                                {
                                    using (ArmoredOutputStream armoredStream = new ArmoredOutputStream(outputStream))
                                    {
                                        using (Stream cOut = cPk.Open(armoredStream, bytes.Length))
                                        {
                                            cOut.Write(bytes, 0, bytes.Length);
                                        }
                                    }
                                }
                                else
                                {
                                    using (Stream cOut = cPk.Open(outputStream, bytes.Length))
                                    {
                                        cOut.Write(bytes, 0, bytes.Length);
                                    }
                                }
                            }
                        }
                    }
                }
                catch (PgpException e)
                {
                    throw;
                }
            }
    
            #endregion Encrypt
    
            #region Encrypt and Sign
    
            /*
             * Encrypt and sign the file pointed to by unencryptedFileInfo and
             */
    
            public static void EncryptAndSign(string inputFile, string outputFile, string publicKeyFile, string privateKeyFile, string passPhrase, bool armor)
            {
                PgpEncryptionKeys encryptionKeys = new PgpEncryptionKeys(publicKeyFile, privateKeyFile, passPhrase);
    
                if (!File.Exists(inputFile))
                    throw new FileNotFoundException(String.Format("Input file [{0}] does not exist.", inputFile));
    
                if (!File.Exists(publicKeyFile))
                    throw new FileNotFoundException(String.Format("Public Key file [{0}] does not exist.", publicKeyFile));
    
                if (!File.Exists(privateKeyFile))
                    throw new FileNotFoundException(String.Format("Private Key file [{0}] does not exist.", privateKeyFile));
    
                if (String.IsNullOrEmpty(passPhrase))
                    throw new ArgumentNullException("Invalid Pass Phrase.");
    
                if (encryptionKeys == null)
                    throw new ArgumentNullException("Encryption Key not found.");
    
                using (Stream outputStream = File.Create(outputFile))
                {
                    if (armor)
                        using (ArmoredOutputStream armoredOutputStream = new ArmoredOutputStream(outputStream))
                        {
                            OutputEncrypted(inputFile, armoredOutputStream, encryptionKeys);
                        }
                    else
                        OutputEncrypted(inputFile, outputStream, encryptionKeys);
                }
            }
    
            private static void OutputEncrypted(string inputFile, Stream outputStream, PgpEncryptionKeys encryptionKeys)
            {
                using (Stream encryptedOut = ChainEncryptedOut(outputStream, encryptionKeys))
                {
                    FileInfo unencryptedFileInfo = new FileInfo(inputFile);
                    using (Stream compressedOut = ChainCompressedOut(encryptedOut))
                    {
                        PgpSignatureGenerator signatureGenerator = InitSignatureGenerator(compressedOut, encryptionKeys);
                        using (Stream literalOut = ChainLiteralOut(compressedOut, unencryptedFileInfo))
                        {
                            using (FileStream inputFileStream = unencryptedFileInfo.OpenRead())
                            {
                                WriteOutputAndSign(compressedOut, literalOut, inputFileStream, signatureGenerator);
                                inputFileStream.Close();
                            }
                        }
                    }
                }
            }
    
            private static void WriteOutputAndSign(Stream compressedOut, Stream literalOut, FileStream inputFile, PgpSignatureGenerator signatureGenerator)
            {
                int length = 0;
                byte[] buf = new byte[BufferSize];
                while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
                {
                    literalOut.Write(buf, 0, length);
                    signatureGenerator.Update(buf, 0, length);
                }
                signatureGenerator.Generate().Encode(compressedOut);
            }
    
            private static Stream ChainEncryptedOut(Stream outputStream, PgpEncryptionKeys m_encryptionKeys)
            {
                PgpEncryptedDataGenerator encryptedDataGenerator;
                encryptedDataGenerator = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.TripleDes, new SecureRandom());
                encryptedDataGenerator.AddMethod(m_encryptionKeys.PublicKey);
                return encryptedDataGenerator.Open(outputStream, new byte[BufferSize]);
            }
    
            private static Stream ChainCompressedOut(Stream encryptedOut)
            {
                PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
                return compressedDataGenerator.Open(encryptedOut);
            }
    
            private static Stream ChainLiteralOut(Stream compressedOut, FileInfo file)
            {
                PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator();
                return pgpLiteralDataGenerator.Open(compressedOut, PgpLiteralData.Binary, file);
            }
    
            private static PgpSignatureGenerator InitSignatureGenerator(Stream compressedOut, PgpEncryptionKeys m_encryptionKeys)
            {
                const bool IsCritical = false;
                const bool IsNested = false;
                PublicKeyAlgorithmTag tag = m_encryptionKeys.SecretKey.PublicKey.Algorithm;
                PgpSignatureGenerator pgpSignatureGenerator = new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha1);
                pgpSignatureGenerator.InitSign(PgpSignature.BinaryDocument, m_encryptionKeys.PrivateKey);
                foreach (string userId in m_encryptionKeys.SecretKey.PublicKey.GetUserIds())
                {
                    PgpSignatureSubpacketGenerator subPacketGenerator = new PgpSignatureSubpacketGenerator();
                    subPacketGenerator.SetSignerUserId(IsCritical, userId);
                    pgpSignatureGenerator.SetHashedSubpackets(subPacketGenerator.Generate());
                    // Just the first one!
                    break;
                }
                pgpSignatureGenerator.GenerateOnePassVersion(IsNested).Encode(compressedOut);
                return pgpSignatureGenerator;
            }
    
            #endregion Encrypt and Sign
    
            #region Decrypt
    
            /*
           * decrypt a given stream.
           */
    
            public static void Decrypt(string inputfile, string privateKeyFile, string passPhrase, string outputFile)
            {
                if (!File.Exists(inputfile))
                    throw new FileNotFoundException(String.Format("Encrypted File [{0}] not found.", inputfile));
    
                if (!File.Exists(privateKeyFile))
                    throw new FileNotFoundException(String.Format("Private Key File [{0}] not found.", privateKeyFile));
    
                if (String.IsNullOrEmpty(outputFile))
                    throw new ArgumentNullException("Invalid Output file path.");
    
                using (Stream inputStream = File.OpenRead(inputfile))
                {
                    using (Stream keyIn = File.OpenRead(privateKeyFile))
                    {
                        Decrypt(inputStream, keyIn, passPhrase, outputFile);
                    }
                }
            }
    
            /*
            * decrypt a given stream.
            */
    
            public static void Decrypt(Stream inputStream, Stream privateKeyStream, string passPhrase, string outputFile)
            {
                try
                {
                    PgpObjectFactory pgpF = null;
                    PgpEncryptedDataList enc = null;
                    PgpObject o = null;
                    PgpPrivateKey sKey = null;
                    PgpPublicKeyEncryptedData pbe = null;
                    PgpSecretKeyRingBundle pgpSec = null;
    
                    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())
                    {
                        sKey = FindSecretKey(pgpSec, pked.KeyId, passPhrase.ToCharArray());
    
                        if (sKey != null)
                        {
                            pbe = pked;
                            break;
                        }
                    }
    
                    if (sKey == null)
                        throw new ArgumentException("Secret key for message not found.");
    
                    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.");
    
                    #region commented code
    
                    //if (pbe.IsIntegrityProtected())
                    //{
                    //    if (!pbe.Verify())
                    //        msg = "message failed integrity check.";
                    //    //Console.Error.WriteLine("message failed integrity check");
                    //    else
                    //        msg = "message integrity check passed.";
                    //    //Console.Error.WriteLine("message integrity check passed");
                    //}
                    //else
                    //{
                    //    msg = "no message integrity check.";
                    //    //Console.Error.WriteLine("no message integrity check");
                    //}
    
                    #endregion commented code
                }
                catch (PgpException ex)
                {
                    throw;
                }
            }
    
            #endregion Decrypt
    
            #region Private helpers
    
            /*
            * A simple routine that opens a key ring file and loads the first available key suitable for encryption.
            */
    
            private static PgpPublicKey ReadPublicKey(Stream inputStream)
            {
                inputStream = PgpUtilities.GetDecoderStream(inputStream);
    
                PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(inputStream);
    
                // we just loop through the collection till we find a key suitable for encryption, in the real
                // world you would probably want to be a bit smarter about this.
                // iterate through the key rings.
                foreach (PgpPublicKeyRing kRing in pgpPub.GetKeyRings())
                {
                    foreach (PgpPublicKey k in kRing.GetPublicKeys())
                    {
                        if (k.IsEncryptionKey)
                            return k;
                    }
                }
    
                throw new ArgumentException("Can't find encryption key in key ring.");
            }
    
            /*
            * Search a secret key ring collection for a secret key corresponding to keyId if it exists.
            */
    
            private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyId, char[] pass)
            {
                PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyId);
    
                if (pgpSecKey == null)
                    return null;
    
                return pgpSecKey.ExtractPrivateKey(pass);
            }
    
            #endregion Private helpers
        }
    }
    

    And it uses:

    using System;
    using System.IO;
    using System.Linq;
    using Org.BouncyCastle.Bcpg.OpenPgp;
    
    namespace FileEncryptionTasks.Helpers.PGP
    {
        public class PgpEncryptionKeys
        {
            public PgpPublicKey PublicKey { get; private set; }
    
            public PgpPrivateKey PrivateKey { get; private set; }
    
            public PgpSecretKey SecretKey { get; private set; }
    
            /// <summary>
            /// Initializes a new instance of the EncryptionKeys class.
            /// Two keys are required to encrypt and sign data. Your private key and the recipients public key.
            /// The data is encrypted with the recipients public key and signed with your private key.
            /// </summary>
            /// <param name="publicKeyPath">The key used to encrypt the data</param>
            /// <param name="privateKeyPath">The key used to sign the data.</param>
            /// <param name="passPhrase">The (your) password required to access the private key</param>
            /// <exception cref="ArgumentException">Public key not found. Private key not found. Missing password</exception>
            public PgpEncryptionKeys(string publicKeyPath, string privateKeyPath, string passPhrase)
            {
                if (!File.Exists(publicKeyPath))
                    throw new ArgumentException("Public key file not found", "publicKeyPath");
                if (!File.Exists(privateKeyPath))
                    throw new ArgumentException("Private key file not found", "privateKeyPath");
                if (String.IsNullOrEmpty(passPhrase))
                    throw new ArgumentException("passPhrase is null or empty.", "passPhrase");
                PublicKey = ReadPublicKey(publicKeyPath);
                SecretKey = ReadSecretKey(privateKeyPath);
                PrivateKey = ReadPrivateKey(passPhrase);
            }
    
            #region Secret Key
    
            private PgpSecretKey ReadSecretKey(string privateKeyPath)
            {
                using (Stream keyIn = File.OpenRead(privateKeyPath))
                {
                    using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn))
                    {
                        PgpSecretKeyRingBundle secretKeyRingBundle = new PgpSecretKeyRingBundle(inputStream);
                        PgpSecretKey foundKey = GetFirstSecretKey(secretKeyRingBundle);
                        if (foundKey != null)
                            return foundKey;
                    }
                }
                throw new ArgumentException("Can't find signing key in key ring.");
            }
    
            /// <summary>
            /// Return the first key we can use to encrypt.
            /// Note: A file can contain multiple keys (stored in "key rings")
            /// </summary>
            private PgpSecretKey GetFirstSecretKey(PgpSecretKeyRingBundle secretKeyRingBundle)
            {
                foreach (PgpSecretKeyRing kRing in secretKeyRingBundle.GetKeyRings())
                {
                    PgpSecretKey key = kRing.GetSecretKeys()
                        .Cast<PgpSecretKey>()
                        .Where(k => k.IsSigningKey)
                        .FirstOrDefault();
                    if (key != null)
                        return key;
                }
                return null;
            }
    
            #endregion Secret Key
    
            #region Public Key
    
            private PgpPublicKey ReadPublicKey(string publicKeyPath)
            {
                using (Stream keyIn = File.OpenRead(publicKeyPath))
                {
                    using (Stream inputStream = PgpUtilities.GetDecoderStream(keyIn))
                    {
                        PgpPublicKeyRingBundle publicKeyRingBundle = new PgpPublicKeyRingBundle(inputStream);
                        PgpPublicKey foundKey = GetFirstPublicKey(publicKeyRingBundle);
                        if (foundKey != null)
                            return foundKey;
                    }
                }
                throw new ArgumentException("No encryption key found in public key ring.");
            }
    
            private PgpPublicKey GetFirstPublicKey(PgpPublicKeyRingBundle publicKeyRingBundle)
            {
                foreach (PgpPublicKeyRing kRing in publicKeyRingBundle.GetKeyRings())
                {
                    PgpPublicKey key = kRing.GetPublicKeys()
                        .Cast<PgpPublicKey>()
                        .Where(k => k.IsEncryptionKey)
                        .FirstOrDefault();
                    if (key != null)
                        return key;
                }
                return null;
            }
    
            #endregion Public Key
    
            #region Private Key
    
            private PgpPrivateKey ReadPrivateKey(string passPhrase)
            {
                PgpPrivateKey privateKey = SecretKey.ExtractPrivateKey(passPhrase.ToCharArray());
                if (privateKey != null)
                    return privateKey;
                throw new ArgumentException("No private key found in secret key.");
            }
    
            #endregion Private Key
        }
    }
    

    Encrypt a file:

    PGPEncryptDecrypt.EncryptFile(inputFileName, 
                                  outputFileName,
                                  recipientKeyFileName,
                                  shouldArmor,
                                  shouldCheckIntegrity);
    

    Decrypt a file:

    PGPEncryptDecrypt.Decrypt(inputFileName, 
                              privateKeyFileName,
                              passPhrase, 
                              outputFileName);
    
    0 讨论(0)
提交回复
热议问题