Uzay
Uzay

Reputation: 31

Decrypt PGP/MIME Message with BouncyCastle

I started to search for a JAVA lib which helps me to encrypt/decrypt MIME messages with OpenPGP and found BouncyCastle. While trying to understand how the lib works I successfully decrypted OpenPGP encrypted MIME message of type PGP/Inline. Now I try to find out if I can also decrypt with BouncyCastle MIME Messages of type PGP/MIME. I searched a lot in the web and could not find any hint if this is possible at all and if so how to do it. So I decided to put the question here. Can anyone give a hint on this topic?

Thank you in advance!

Upvotes: 2

Views: 1437

Answers (2)

Dizzle
Dizzle

Reputation: 21

Decrypt using Keys in files (generally .asc files), or from Streams (depending on how your company manages keys, using Bouncy Castle:

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

namespace Cryptor.Agents
{
    /// <summary>
    /// Decrypts a specified PGP Encrypted File with the specified Keys and Password
    /// </summary>
    public class PgpDecryptionAgent
    {
        private readonly string _encryptedFilePath;
        private readonly string _outputDirectory;
        private readonly PgpEncryptionKeys _pgpKeys;

        /// <summary>
        /// Constructs the PgpDecryptionAgent with PGP Keys in File
        /// </summary>
        /// <param name="encryptedFilePath">The path to the encrypted file</param>
        /// <param name="outputDirectory">The directory to which the file will be decrypted</param>
        /// <param name="publicKeyPath">The path to the public key</param>
        /// <param name="privateKeyPath">The path to the private key</param>
        /// <param name="password">The password to access the Private key</param>
        public PgpDecryptionAgent(string encryptedFilePath, string outputDirectory, string publicKeyPath, string privateKeyPath, string password)
        {
            _encryptedFilePath = encryptedFilePath;
            _outputDirectory = outputDirectory;
            _pgpKeys = new PgpEncryptionKeys(publicKeyPath, privateKeyPath, password);
        }

        /// <summary>
        /// Constructs the PgpDecryptionAgent with PGP Keys in Stream
        /// </summary>
        /// <param name="encryptedFilePath">The path to the encrypted file</param>
        /// <param name="outputDirectory">The directory to which the file will be decrypted</param>
        /// <param name="publicKey">Stream of the Public key</param>
        /// <param name="privateKey">Stream of the Private key</param>
        /// <param name="password">The password to access the Private key</param>
        public PgpDecryptionAgent(string encryptedFilePath, string outputDirectory, Stream publicKey, Stream privateKey, string password)
        {
            _encryptedFilePath = encryptedFilePath;
            _outputDirectory = outputDirectory;
            _pgpKeys = new PgpEncryptionKeys(publicKey, privateKey, password);
        }

        /// <summary>
        /// Performs the Decryption operation
        /// </summary>
        public void DecryptPgpEncryptedFile()
        {
            FileStream encryptedFile = new FileStream(_encryptedFilePath, FileMode.Open, FileAccess.Read);
            DecryptFile(encryptedFile, _outputDirectory);
        }

        /// <summary>
        /// Performs the Decryption operation
        /// </summary>
        /// <param name="input">The encrypted file input</param>
        /// <param name="outputpath">The decrypted file output</param>
        private void DecryptFile(Stream input, string outputpath)
        {
            input = PgpUtilities.GetDecoderStream(input);
            try
            {
                PgpObjectFactory pgpObjF = new PgpObjectFactory(input);
                PgpEncryptedDataList enc;
                PgpObject obj = pgpObjF.NextPgpObject();
                var list = obj as PgpEncryptedDataList;
                if (list != null)
                {
                    enc = list;
                }
                else
                {
                    enc = (PgpEncryptedDataList) pgpObjF.NextPgpObject();
                }
                PgpPrivateKey privKey = _pgpKeys.PrivateKey;
                PgpPublicKeyEncryptedData pbe = enc.GetEncryptedDataObjects().Cast<PgpPublicKeyEncryptedData>().FirstOrDefault(pked => privKey != null);
                if (pbe != null)
                {
                    Stream clear = pbe.GetDataStream(privKey);
                    PgpObjectFactory plainFact = new PgpObjectFactory(clear);
                    PgpObject message = plainFact.NextPgpObject();
                    var pgpCompressedData = message as PgpCompressedData;
                    if (pgpCompressedData == null) return;
                    PgpCompressedData cData = pgpCompressedData;
                    Stream compDataIn = cData.GetDataStream();
                    PgpObjectFactory o = new PgpObjectFactory(compDataIn);
                    message = o.NextPgpObject();
                    PgpLiteralData literalData;
                    if (message is PgpOnePassSignatureList)
                    {
                        message = o.NextPgpObject();
                        literalData = (PgpLiteralData) message;
                        Stream output = File.Create(outputpath + "\\" + literalData.FileName);
                        Stream unc = literalData.GetInputStream();
                        Streams.PipeAll(unc, output);
                    }
                    else
                    {
                        literalData = (PgpLiteralData) message;
                        Stream output = File.Create(outputpath + "\\" + literalData.FileName);
                        Stream unc = literalData.GetInputStream();
                        Streams.PipeAll(unc, output);
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// The PGP Encryption Keys
        /// </summary>
        public class PgpEncryptionKeys
        {
            public PgpPublicKey PublicKey { get; private set; }
            public PgpPrivateKey PrivateKey { get; private set; }
            public PgpSecretKey SecretKey { get; private set; }

            /// <summary>
            /// Configure Decryption from Key Streams
            /// </summary>
            /// <param name="publicKey">Stream of the Public Key</param>
            /// <param name="privateKey">Stream of the Private Key</param>
            /// <param name="password">Password to access the Private Key</param>
            public PgpEncryptionKeys(Stream publicKey, Stream privateKey, string password)
            {
                PublicKey = ReadPublicKeyFromStream(publicKey);
                SecretKey = ReadSecretKeyFromStream(privateKey);
                PrivateKey = ReadPrivateKey(password);
            }

            /// <summary>
            /// Configure Decryption from Key Files
            /// </summary>
            /// <param name="publicKeyPath">String path to the Public Key file</param>
            /// <param name="privateKeyPath">String path to the Private key file</param>
            /// <param name="passPhrase">Password to access the Private Key</param>
            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 = ReadPublicKeyFromFile(publicKeyPath);
                SecretKey = ReadSecretKeyFromFile(privateKeyPath);
                PrivateKey = ReadPrivateKey(passPhrase);

            }

            /// <summary>
            /// Gets the Secret Key from a File (generally a .asc file)
            /// </summary>
            /// <param name="privateKeyPath">The path to the Private Key file</param>
            private PgpSecretKey ReadSecretKeyFromFile(string privateKeyPath)
            {
                using (Stream keyIn = File.OpenRead(privateKeyPath))
                {
                    return ReadSecretKeyFromStream(keyIn);
                }
            }

            /// <summary>
            /// Gets the Secret Key from a Stream
            /// </summary>
            /// <param name="keyIn">The Stream of the PGP</param>
            private PgpSecretKey ReadSecretKeyFromStream(Stream keyIn)
            {
                 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>
            /// Obtains the first secret key from the bundle
            /// </summary>
            /// <param name="secretKeyRingBundle"></param>
            /// <returns></returns>
            private PgpSecretKey GetFirstSecretKey(PgpSecretKeyRingBundle secretKeyRingBundle)
            {
                foreach (PgpSecretKeyRing kRing in secretKeyRingBundle.GetKeyRings())
                {
                    PgpSecretKey key = kRing.GetSecretKeys().Cast<PgpSecretKey>().FirstOrDefault(k => k.IsSigningKey);

                    if (key != null)
                        return key;
                }
                return null;
            }

            /// <summary>
            /// Gets the Public Key from a file (generally a .asc file)
            /// </summary>
            /// <param name="publicKeyPath">The path to the Public Key file</param>
            private PgpPublicKey ReadPublicKeyFromFile(string publicKeyPath)
            {
                using (Stream keyIn = File.OpenRead(publicKeyPath))
                {
                    return ReadPublicKeyFromStream(keyIn);
                }
            }

            /// <summary>
            /// Gets the Public Key from a Stream
            /// </summary>
            /// <param name="keyIn">The Stream of the Public Key</param>
            private PgpPublicKey ReadPublicKeyFromStream(Stream keyIn)
            {
                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.");
            }

            /// <summary>
            /// Gets the first Public Key from the bundle
            /// </summary>
            /// <param name="publicKeyRingBundle"></param>
            /// <returns></returns>
            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;
            }

            /// <summary>
            /// Reads the Private Key with the provided password
            /// </summary>
            /// <param name="passPhrase">The password to access the Private Key</param>
            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.");

            }
        }
    }
}

Upvotes: 2

It is possible with the commercial OpenPGP library for Java from DidiSoft:

    PGPMailUtils mailUtil = new PGPMailUtils();
    if (mailUtil.isOpenPGPEncrypted(message)) {

        try {
            MimeMessage decrypted = 
                       mailUtil.decryptMessage(emailSession, 
                                               (MimeMessage)message, 
                                               "c:\\Projects\\PGPKeys\\private.key", 
                                               "changeit");

            Object decryptedRawContent = decrypted.getContent(); 
            if (decryptedRawContent instanceof Multipart) {
                Multipart multipart = (Multipart) decryptedRawContent;

                 for (int j = 0; j < multipart.getCount(); j++) {
                  BodyPart bodyPart = multipart.getBodyPart(j);

                  String disposition = bodyPart.getDisposition();
                  if (disposition != null && (disposition.equalsIgnoreCase("ATTACHMENT"))) { 
                      System.out.println("Mail have some attachment");

                      DataHandler handler = bodyPart.getDataHandler();
                      System.out.println("file name : " + handler.getName());                                 
              // save here the file ...
                  } else { 
              // print the decrypted text then
                      String content = getText(bodyPart);
                      System.out.println(content);
                  }   
                 }
            } else {
                // pgp inline mail, just print the decrypted text then
                String content = message.getContent().toString();
                System.out.println(content);
            }               
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    } else if (mailUtil.isOpenPGPSigned(message)) {
        // pgp signed only message
        MimeBodyPart decrypted = mailUtil.getSignedContent(message);
        String content = getText(decrypted);
        System.out.println(content);
    } else {
        System.out.println(message.getContentType());
    }

Disclaimer: I work for DidiSoft.

Upvotes: 0

Related Questions