PGP Single Pass Sign and Encrypt with Bouncy Castle

Bouncy Castle is a great open source resource. However, the off-the-shelf PGP functionality is severely lacking in real-world-usabaility. Most of what you need is easy enough to code up yourself (and I would love to contribute what I’ve done if I could). One thing that you really need that doesn’t come built-in and is actually quite hard to do right is PGP Single Pass Sign and Encrypt. Here’s a class in the style of csharp\crypto\test\src\openpgp\examples\KeyBasedFileProcessor.cs that does exactly that.

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

namespace PgpCrypto
{
	public class PgpProcessor
	{
		public void SignAndEncryptFile(string actualFileName, string embeddedFileName,
			Stream keyIn, long keyId, Stream outputStream,
			char[] password, bool armor, bool withIntegrityCheck, PgpPublicKey encKey)
		{
			const int BUFFER_SIZE = 1 << 16; // should always be power of 2

			if (armor)
				outputStream = new ArmoredOutputStream(outputStream);

			// Init encrypted data generator
			PgpEncryptedDataGenerator encryptedDataGenerator =
				new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
			encryptedDataGenerator.AddMethod(encKey);
			Stream encryptedOut = encryptedDataGenerator.Open(outputStream, new byte[BUFFER_SIZE]);

			// Init compression
			PgpCompressedDataGenerator compressedDataGenerator = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
			Stream compressedOut = compressedDataGenerator.Open(encryptedOut);

			// Init signature
			PgpSecretKeyRingBundle pgpSecBundle = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyIn));
			PgpSecretKey pgpSecKey = pgpSecBundle.GetSecretKey(keyId);
			if (pgpSecKey == null)
				throw new ArgumentException(keyId.ToString("X") + " could not be found in specified key ring bundle.", "keyId");
			PgpPrivateKey pgpPrivKey = pgpSecKey.ExtractPrivateKey(password);
			PgpSignatureGenerator signatureGenerator = new PgpSignatureGenerator(pgpSecKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
			signatureGenerator.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
			foreach (string userId in pgpSecKey.PublicKey.GetUserIds())
			{
				PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
				spGen.SetSignerUserId(false, userId);
				signatureGenerator.SetHashedSubpackets(spGen.Generate());
				// Just the first one!
				break;
			}
			signatureGenerator.GenerateOnePassVersion(false).Encode(compressedOut);

			// Create the Literal Data generator output stream
			PgpLiteralDataGenerator literalDataGenerator = new PgpLiteralDataGenerator();
			FileInfo embeddedFile = new FileInfo(embeddedFileName);
			FileInfo actualFile = new FileInfo(actualFileName);
			// TODO: Use lastwritetime from source file
			Stream literalOut = literalDataGenerator.Open(compressedOut, PgpLiteralData.Binary,
				embeddedFile.Name, actualFile.LastWriteTime, new byte[BUFFER_SIZE]);

			// Open the input file
			FileStream inputStream = actualFile.OpenRead();

			byte[] buf = new byte[BUFFER_SIZE];
			int len;
			while ((len = inputStream.Read(buf, 0, buf.Length)) > 0)
			{
				literalOut.Write(buf, 0, len);
				signatureGenerator.Update(buf, 0, len);
			}

			literalOut.Close();
			literalDataGenerator.Close();
			signatureGenerator.Generate().Encode(compressedOut);
			compressedOut.Close();
			compressedDataGenerator.Close();
			encryptedOut.Close();
			encryptedDataGenerator.Close();
			inputStream.Close();

			if (armor)
				outputStream.Close();
		}
	}
}

18 Responses to “PGP Single Pass Sign and Encrypt with Bouncy Castle”

  1. Compare Files Method in C# Unit Testing « John Opincar’s Blue Corner Says:

    [...] Files Method in C# Unit Testing May 6, 2008 — jopincar I’ve been working on a class that wraps the low-level PGP crypto capabilities of Bouncy Castle and presents a higher-level, [...]

  2. Bradguru Says:

    This example doesn’t work.

    Signer SHA1WITHELGAMAL not recognised.

  3. Bradguru Says:

    Nevermind. I got the keys mixed up. It should have been a DSA key sorry.

  4. Bookmarks about Pgp Says:

    [...] – bookmarked by 6 members originally found by ndb3dgj on 2008-11-06 PGP Single Pass Sign and Encrypt with Bouncy Castle http://jopinblog.wordpress.com/2008/06/23/pgp-single-pass-sign-and-encrypt-with-bouncy-castle/ – [...]

  5. PGP Zip Encrypted Files With C# - .NET Geek Says:

    [...] We found a few samples online, but nothing I felt comfortable to use in our codebase. Credits to John Opincar who published a post on single pass encryption and signing. We used the blog post of his, the [...]

  6. Girish Says:

    Thanks for sharing your code, Do you have a single pass decryption for a signed and encrypted file.
    Thanks,

  7. Aaron Says:

    Thank you. I ported this for a Java project I was working on. It solved my problem.

  8. PGP Single Pass Sign and Encrypt .NET Stream « Shamsul Amry's Brain Dump Says:

    [...] to figure out how to do things. I was lazy at that time, and managed to find this blog entry: PGP Single Pass Sign and Encrypt with Bouncy Castle that provided me with the code to encrypt file contents using [...]

  9. BARBOSS Says:

    question: How we can get KeyId?

  10. DodleUp Says:

    Why do you use compression after Decryption?

  11. Tom Says:

    Thank You for your contribution, it’s appreciated!

  12. twoby4 Says:

    Great job!

    Below is a working java version (you will find the “readSecretKey” method in the BC PGPExampleUtil class in source package org.bouncycastle.openpgp.examples):

    public static void signAndEncryptFile(String actualFileName, String embeddedFileName,
    InputStream keyIn, OutputStream outputStream,
    char[] password, boolean armor, boolean withIntegrityCheck, PGPPublicKey encKey) throws Exception {

    final int BUFFER_SIZE = 1 < 0) {
    literalOut.write(buf, 0, len);
    signatureGenerator.update(buf, 0, len);
    }
    literalOut.close();
    literalDataGenerator.close();
    signatureGenerator.generate().encode(compressedOut);
    compressedOut.close();
    compressedDataGenerator.close();
    encryptedOut.close();
    encryptedDataGenerator.close();
    inputStream.close();
    if(armor) {
    outputStream.close();
    }
    }

    • Petter Says:

      twoby4: do you have the code complete somewhere? Parts seems to be missing, I would appreciate it very much! :)

  13. Sumit Says:

    Hi,
    What is this “embeddedFileName” in the signAndEncryptFile method.Is this the file name after encryption or something else.Please clarify.

    Thanks
    Sumit


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.