Decrypting files with Gpg4win using C#

#

Here’s a simple way to implement basic GNU Privacy Guard (GnuPG) decryption in .NET using C# and Gpg4win. This was my first encounter with automated GnuPG decryption and it was designed to run as a script task in SQL Server Integration Services (SSIS). However, here I’ve laid it out as a standalone script that can be pasted into a .cs file and compiled, making it easier to play around with.

About Gpg4win

Gpg4win is an open source tool kit for encrypting and decrypting files and emails using GnuPG. The software download includes a number of optional components to choose from during installation. Only the core component is required for our current purpose; however the Kleopatra key manager might be useful if you need to work with many different keys and the Gpg4win Compendium is a useful document, giving an excellent overview that includes an introductory section for those new to cryptographic principles and terminology.

Creating a key and encrypting a file

To demonstrate the script we first need to create a key and use it to encrypt our file, which we can accomplish using the command prompt and the following commands. You will need to install Gpg4Win first.

1. Create a file to work with:

echo This text is secret. >>MySecret.txt

2. Create a key to protect it:

 gpg2 --gen-key

This initiates an interactive process, prompting for the specification of your key. Since this a test session, you can stick with the defaults where offered, and choose no expiry date. When GPG asks for identity details, use these values:

  • Real name: JohnSmith
  • Email address: john.smith@unrealemail
  • Comment: testing
  • Passphrase: BadPassword

3. If you want to see your new secret key:

gpg2 --list-secret-keys

4. Now use the key to encrypt your file.

gpg2 --encrypt --recipient JohnSmith MySecret.txt

This will create a file called MySecret.txt.gpg, which you may wish to open to see it is encrypted.

5. Export the secret key to a file called JohnSmith.gpg (simulating the fact that our automated process may need to import it).

gpg2 --export-secret-keys --output JohnSmith.gpg JohnSmith

6. Remove the secret key from gpg’s internal store, so that our file cannot be decrypted without first importing it.

gpg2 --delete-secret-key JohnSmith

This will prompt twice to check that you really want to delete your key.

7. Finally, delete the original, unencrypted file.

del MySecret.txt

Command line solution

Now lets look at the manual solution with the command prompt.

1. Import the key from the external file.

gpg2 --import JohnSmith.gpg

2. Decrypt the encrypted file.

gpg2 --output MySecret.txt --batch --passphrase "BadPassword" --decrypt MySecret.txt.gpg

And that’s it! Our readable MySecret.txt file has been recreated.

The C# script

Here’s the script to automate the command line solution. I’ve added a few console outputs to provide information when it is run as a compiled executable. For the SSIS script task version these would be removed and possibly replaced with native SSIS logging events.

// Consolidata Ltd
// 27 May 2015 - Roger Light - Created
// Usage: GpgDecrypt "EncryptedFilePath" "KeyPath" "PassPhrase"
 
using System;
using System.Diagnostics;
using System.IO;

class GpgDecrypt
{
  public static void Main(string[] param)
  {
    string encryptedFilePath = param[0];
    string keyPath = param[1];
    string passPhrase = param[2];
    string decryptedFilePath = Path.Combine (
      Path.GetDirectoryName(encryptedFilePath),
      Path.GetFileNameWithoutExtension(encryptedFilePath)
      );
    string gpgImportError = "";
    string gpgDecryptError = "";

    // Create a process to run Gpg4win.
    var gpg = new Process();
    gpg.StartInfo.FileName = "gpg2";
    gpg.StartInfo.UseShellExecute = false;
    gpg.StartInfo.RedirectStandardError = true;

    // Import the key.
    gpg.StartInfo.Arguments = " --import " + keyPath;

    gpg.Start();
    gpgImportError = gpg.StandardError.ReadToEnd();
    gpg.WaitForExit();
    Console.WriteLine("Import result: " + gpgImportError);

    // Decrypt the file.
    gpg.StartInfo.Arguments = " --output \"" + decryptedFilePath +
      "\" --batch --passphrase \"" + passPhrase + 
      "\" --decrypt \"" + encryptedFilePath + "\"";
    
    gpg.Start();
    gpgDecryptError = gpg.StandardError.ReadToEnd();
    gpg.WaitForExit();
    Console.WriteLine("Decryption result: " + gpgDecryptError);

    // Remove the original file.
    File.Delete(encryptedFilePath);
    Console.WriteLine("Decrypted file: " + decryptedFilePath);
  }
}

The script takes three parameters: the encrypted file path, the key path and the pass phrase. So, if we were working in a directory called C:\Test, the command to replicate our solution above would like this:

GpgDecrypt C:\Test\MySecret.txt.gpg C:\Test\JohnSmith.gpg "BadPassword"

To test the script you can save it as a .cs file and then compile it using the C# compiler (csc.exe). Depending on your development environment, this should be as easy as:

csc GpgDecrypt.cs

If you are having trouble running the compiler, you can find some useful tips here.

Improvements

The script is deliberately simple for the purpose of demonstration. Two obvious potential improvements would be more robust handling of the parameters, to support file paths containing spaces for example, and some proper error handling.

In the long run, I plan to look at storing the key and pass phrase securely in a database, and also at implementing the encryption process to support sending encrypted output files from our data platform back to a client.

Resources

I found a great deal of useful information in the documentation for Gpg4win and also GnuGP itself. The Gpg4win forum (sign up required) was very helpful. I also came across an alternative implementation that involved invoking the command line from C# rather than working with Gpg4win directly, which may be of interest to some.