Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Automated File Decryption Using GnuPG and C#

3.27/5 (9 votes)
11 Dec 2007CPOL2 min read 1  
How To: Automate decryption of PGP / GnuPG encrypted files.

Introduction

Suppose you need to decrypt and process a PGP / GnuPG encrypted file. This brief how-to will hopefully help you do that.

Background

The set up: our client needed to provide us - securely - with an XML file made available via FTP, daily. Secure FTP was not an option, and they insisted on PGP. No problem, right? Well, actually quite a few problems, which I will attempt to help you avoid here.

Obviously, you need to install PGP or its open-source pal, GnuPG. You need to create a private and public key for yourself, and provide the public key to the "encryptor". All this stuff is well documented on various sites, so I'm not going into it here. There's a nice little Windows app called "GNU Privacy Assistant" (I used version v0.5.0) that will make this job fast and easy. Google it, or get it through www.gnupg.org.

End result: a human-unreadable / GPG-encrypted document that now needs to be decrypted and processed by an automated process.

Using the code

Here's the method we use. Basically, grab your encrypted file from the file system and get a reference to it through the FileInfo class. Pass that to the method.

C#
private string DecryptFile(FileInfo encryptedFile)
{
  // decrypts the file using GnuPG, saves it to the file system
  // and returns the new (decrypted) file name.

  // encrypted file: thefile.xml.gpg decrypted: thefile.xml

  string outputFileName = this.GetDecryptedFileName(encryptedFile);
  // whatever you want here - just a convention

  string path = encryptedFile.DirectoryName;
  string outputFileNameFullPath = path + "\\" + outputFileName;
  try {
      System.Diagnostics.ProcessStartInfo psi = 
        new System.Diagnostics.ProcessStartInfo("cmd.exe");
      psi.CreateNoWindow = true;
      psi.UseShellExecute = false;
      psi.RedirectStandardInput = true;
      psi.RedirectStandardOutput = true;
      psi.RedirectStandardError = true;
      psi.WorkingDirectory = "C:\\Program Files\\GnuPP\\GnuPG";

      System.Diagnostics.Process process = System.Diagnostics.Process.Start(psi);
      // actually NEED to set this as a local string variable
      // and pass it - bombs otherwise!
      string sCommandLine = "echo " + this._passphrase + 
                            "| gpg.exe --passphrase-fd 0 -o \"" +
      outputFileNameFullPath + "\" --decrypt \"" + 
                               encryptedFile.FullName + "\"";
      process.StandardInput.WriteLine(sCommandLine);
      process.StandardInput.Flush();
      process.StandardInput.Close();
      process.WaitForExit();
      process.Close();
 }
 catch (Exception ex)
 {
  this.HandleError(ex);
 }
 return outputFileName;
}

GetDecryptedFileName() just provides a name for the resulting decrypted file - it's implementation is not important. Basically, just lop the ".gpg" extension off the end of the encrypted file. The method returns the decrypted file name for further processing; again, this isn't important, and simply suited the purpose at hand.

The interesting stuff: notice we call up the Windows Command EXE using System.Diagnostics.ProcessStartInfo. The working directory is where the GnuPG application (EXE) and the public and private key files are stored.

The tricky part is passing the "secret" passphrase and options and commands to the gpg.exe process via cmd.exe. This is done through System.Diagnostics.Process.StandardInput. Essentially, you are spawning a command window and feeding it something like:

C:\> echo my passphrase goes here| gpg.exe --passphrase-fd 0 
     -o "myDecryptedOutputFileName" --decrypt "myEncryptedFileName"

Problem solved! Note the security issues with having your private key passphrase used in an application like this - that's your issue! Not a big problem for our situation.

Points of interest

Notice the "-o" option? Don't use "-output", it won't work. Also notice how we build the whole command in "sCommandLine" and pass this variable to WriteLine? Also important. For some reason, building this directly in StandardInput.WriteLine() doesn't work either - probably the escape characters in C#.

Hope that helps you avoid the frustrations we went through in coming up with this solution!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)