I had a hard time finding a good sample of decrypting a PGP-encrypted file in C# so I decided to write this post.
Bouncy Castle works with streams for the most part. So first you need to open the file as a stream, then get a special decoder stream using PgpUtilities:
// Comment
using (FileStream fsEncryptedFile = File.Open(ENCRYPTED_FILENAME, FileMode.Open)) {
using (Stream decoderStream = PgpUtilities.GetDecoderStream(fsEncryptedFile))
{
Next we have to create a PgpObjectFactory using that stream which will provide us with PgpObjects. We're looking for a PgpEncryptedDataList so we might have to skip the first object which could be a marker:
PgpObjectFactory factory = new PgpObjectFactory(decoderStream);
PgpObject obj = factory.NextPgpObject();
if (!(obj is PgpEncryptedDataList))
{
obj = factory.NextPgpObject(); // first object might be a PGP marker packet
}
PgpEncryptedDataList edl = obj as PgpEncryptedDataList;
With our PgpEncryptedDataList we find PgpPublicKeyEncryptedData, and find the key. Here we open a secret keyring file and find the key based on the KeyId in the data and decrypt it using a passphrase:
PgpEncryptedDataList edl = obj as PgpEncryptedDataList;
foreach (PgpPublicKeyEncryptedData data in edl.GetEncryptedDataObjects())
{
PgpPrivateKey privateKey = null;
using (FileStream fsKeyring = File.Open(KEYRING_FILENAME, FileMode.Open))
{
PgpSecretKeyRingBundle bundle = new PgpSecretKeyRingBundle(fsKeyring );
PgpSecretKey secretKey = bundle.GetSecretKey(data.KeyId);
privateKey = secretKey.ExtractPrivateKey(PASSPHRASE.ToCharArray());
}
Next we use GetDataStream on our PgpPublicKeyEncryptedData and pass it our private key and create a PgpObjectFactory that will give us our data. If the object is compressed, we uncompress it:
PgpObjectFactory plainFactory = new PgpObjectFactory(data.GetDataStream(privateKey));
PgpObject message = plainFactory.NextPgpObject();
if (message is PgpCompressedData) {
message = new PgpObjectFactory(((PgpCompressedData)message).GetDataStream()).NextPgpObject();
}
Hopefully that will give us a PgpLiteralData which is what we need to get a stream containing the plain, unencrypted data and write that to a new file:
if (message is PgpLiteralData) {
PgpLiteralData ld = (PgpLiteralData)message;
using (FileStream outStream = File.Create(OUTPUT_FILENAME)) {
Stream inStream = ld.GetInputStream();
byte [] buffer = new byte[0x100000];
int count = inStream.Read(buffer, 0, buffer.Length);
while (count > 0) {
outStream.Write(buffer, 0, count);
count = inStream.Read(buffer, 0, buffer.Length);
}
outStream.Close();
}
} else {
Console.WriteLine("ERROR: Unknown type of message in PGP file: {0}", message.GetType().FullName);
}