Tuesday, August 7, 2007

PGP Pipeline Component v1.1

I ran across a few things that I needed to update and have posted them here as well.

Notable changes include:

  • Added Pipeline Component property of Extension. This allows you to specify what extension you want to place at the end of your encrypted file. The default value is PGP.
  • Added capability to decrypt a signed message.
  • Updated decryption to handle other than .PGP extension. Previously hard coded to remove only the .pgp from the filename.
  • Updated TestFixture form to be more user friendly. You can now specify where you want your output file to be generated.
  • Minor code changes that don't necessarily affect logic, but may improve performance.

Link to readme.txt: readme.txt
Link to dll: BAJ.BizTalk.PipelineComponent.PGP.dll
Link to source code: PGP.zip

[UPDATED - 9/11/2007] - It was brought to my attention that I did not include instructions for obtaining the crypto.dll file. In my original post, I mentioned that you had to download the Bouncy Castle source code as I didn't feel it was appropriate for me to distribute it. Also, you will need to strongly name the assembly. I have updated the readme.txt file with the same message. Sorry for any confusion.

[UPDATED - 7/28/2009] - File locations have been updated and should be available for download.


Matt said...

I think that I have spotted a problem in the code in relation to the design time versus instance/deployment time properties.

As the code stands, it works if you set the properties at design time. It also works if you change ALL the properties from the default after deployment.

The problem arises when you change just some of the properties once on a send/receive port.

You can see this if, for example, you change the public key name once the component has been used on a pipeline. You get an error "Invalid Operation". If you change the operation from encrypt to decrypt, then it complains about an invalid path.

I believe that the issue is in IPersistPropertyBag.Load. The problem arises when the code reads initially the design time value and then reads the instance time setting (two executions always occur per invocation).

The code will read a design time setting and then (if not changed at deployment time) will get null when it reads from the deployment/instance time setting. The code that deals with this is here:

if (valPublicKeyFile != null)
PublicKeyFile = (string)valPublicKeyFile;
PublicKeyFile = "";

All is ok when it gets the design time setting. On BT 2006 when another setting has been changed (e.g. Operation) then the value of valPublicKeyFile (set elsewhere in your code) will be null - at which point you set it to empty string, thereby overwriting the design time setting.

I changed to code to the following to get it work:

if (valPublicKeyFile != null)
PublicKeyFile = (string)valPublicKeyFile;

This should be sufficient as all design time settings, once the component is on a pipeline, are validated when you build the pipeline – it is impossible to build the pipeline unless you have either the string Encrypt or Decrypt as the operation and other values are also valid. You could perform other tests to check that a valid instance/deployment time value has been set (actually only applies to the operation string as Booleans can only be set in a dropdown and there is no way to validate that the keys are valid until you try to use them).

Mail me if you want more explanation!


Matt said...

Sorry, I should have said that this is when I use the component directly in a pipeline and not in an orchestration.

Anonymous said...

Hi Brian,

Can you please check the link for the PGP.zip file, i was not able to download the source from that location.


Brian said...

I just attempted to download the PGP.zip file and it saved and opened successfully. Please try again. If you still experience issues, comment back with your email (blog safe format) and I will email you the file directly.

John said...

I made a small change to the execute method to allow for decryption of gpg files without throwing a null reference exception.

IBaseMessage Microsoft.BizTalk.Component.Interop.IComponent.Execute(IPipelineContext pContext, IBaseMessage pInMsg)
System.Diagnostics.Debug.WriteLine("Begin Execute method for PGP pipeline component.");
IBaseMessagePart bodyPart = pInMsg.BodyPart;
IBaseMessageContext context = pInMsg.Context;
string filename = "";

if (bodyPart != null)
filename = context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties").ToString();

if (filename.Contains("\\")) { filename = filename.Substring(filename.LastIndexOf("\\") + 1); }
if (filename.Contains("/")) { filename = filename.Substring(filename.LastIndexOf("/") + 1); }

if (!Directory.Exists(this.TempDirectory)) { Directory.CreateDirectory(this.TempDirectory); }
filename = Path.Combine(this.TempDirectory, filename);

//System.Diagnostics.EventLog.WriteEntry("PGP", String.Format("Filename is: {0} \r\nOperation is: {1}", filename, this.Operation));
switch (this.operation.ToUpper())
case "ENCRYPT":
Stream encStream = PGPWrapper.EncryptStream(bodyPart.Data, this.PublicKeyFile, filename, this.Extension, this.ASCIIArmorFlag, this.IntegrityCheckFlag);
encStream.Seek(0, SeekOrigin.Begin);
bodyPart.Data = encStream;
context.Write("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", filename + ".pgp");
case "DECRYPT":
Stream decStream = PGPWrapper.DecryptStream(bodyPart.Data, this.PrivateKeyFile, this.Passphrase, filename, this.TempDirectory);
decStream.Seek(0, SeekOrigin.Begin);
bodyPart.Data = decStream;
if (filename .Contains(".pgp")

context.Write("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", filename.Replace(".pgp", ""));

else if (filename .Contains (".gpg"))

context.Write("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties", filename.Replace(".gpg", ""));

throw new Exception("Invalid Operation: " + this.Operation);
catch (System.Exception ex)
//System.Diagnostics.EventLog.WriteEntry("PGP Pipeline Component Error", ex.ToString());
throw ex;

return pInMsg;

hippa said...

Hi Brian
For encrypting the messages for various trading partners in the send pipeline stage, currently using your pipeline i would have to create a separate pipeline for each trading partner with their public key necessary to encrypt the outgoing message. so is it possible to change the key properties into an array or list, so we can mention more than one kep pair and we can select the right keyh pair based on the trading partner identity? please let me know if you have any better ideas for this or should i consider creating separate send pipelines for each trading partner and associate with the respective key pair.

M1k3y said...

The binding file appears to be missing?

Guillaume said...

Hi Brian,

It seems there is a problem with the files to download. The website is not available any more. It would be great if you could post them on an other location.



Calvin Willman said...

Hi Brian,

The site hosting the files is no longer available. It would be great if you could re-host somewhere?


Z said...

Brian - seems that the links are broken again. Could you either send me a copy or fix the links? I would greatly appreciate it.


Anonymous said...

Have you done anything with this for BTS 2010?

Johan said...

Two problems found:

The temp file written before encryption is always encoded using UTF-8.


The property bag load method shouldn't write to properties if the bag value is null.

Code available on request!

Johan said...

And by the way, apart from the problems described above (which hardly has anything to do with biztalk version) it seems to work just fine in Biztalk 2010 using .Net 4.0!

Kavitha Gowda said...

the binding xml is missing. How do i find this.

Chitra Sampathkumar said...

Hi Brain,

I am not able to open the zip file.Seems the link is broken.Can you please send me the code to my email

Brian said...

Please follow this URL. It has the downloadable content. Not sure why BlogSpot moved the files.

Chitra Sampathkumar said...

Thanks for sharing the details