Home > Development > Encryption and decryption with X.509 certificates (with MIME Base64 Encoding)

Encryption and decryption with X.509 certificates (with MIME Base64 Encoding)

We’ve been working on the last months with encyption and decryption using certificates for Biztalk, I haven’t found enough documentation out there but after some time we were able to encrypt and decrypt messages with a very little amount of code.

Messages are encrypted using a certificate’s public key, and decrypted using their private key. This way, to send a message to a particular recipient, he needs to have a certificate with a private key deployed on their side, and you need to have the certificate (only the public key is necessary) deployed on your side. No one will be able to decrypt the message without the private key (it’s an asymmetric encryption/decryption method).

We use this code to encrypt/decrypt messages inside Biztalk Server components, so the code we developed for encryption/decryption uses MIME Base64 Encoding, for example:

Content-ID: {F5BBE1D4-D0E3-4CD7-9B51-1129FA3077E1}
Content-Description: body
Bcc:
MIME-Version: 1.0
Content-type: application/x-pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64

MIAGCSqGSIb3DQEHA6CAMIACAQAxgZEwgY4CAQAwODAkMSIwIAYDVQQDExlSQ0NMIEJpelRhbGsg
q49kxusLITM1r982n2MgZaa8vdgkLBLATSUWDEyDu/B57PZxxxU/AhEyIUppI5fsaxpI7NT+2QPW
8/HT7vfgH0t3ch3AUVglspS/NRYCuaOwG5lIpw9IAAAAAAAAAAAAAA==

How to use the Encrypt method

string messageToEncrypt = "message";
string certificateName = "MyCertificate";
string encryptedMessage = CryptographyHelper.Encrypt(messageToEncrypt, certificateName);

The certificate needs to be deployed on the Personal store inside your Local Machine (the code can be modified in the GetCertificate method to use another store).
To do this deployment, you may want to check this link: http://technet.microsoft.com/en-us/library/cc740068%28WS.10%29.aspx

How to use the Decrypt method

string decryptedMessage =  CryptographyHelper.Decrypt(messageToDecrypt);

This time, the certificate needs to be deployed at the same store but it’ll be necessary to deploy it including the private key. If the method throws an exception “the enveloped data-message does not contain the specified recipient”, this is because the certificate with the private key is not correctly deployed into the current account/local machine personal store.

Source code (download at the bottom)

using System;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.IO;

namespace Logue.Library.Cryptography
{
	public static class CryptographyHelper
	{
		#region Public methods
		public static string Encrypt(string fullMessage, string certificateName)
		{
			X509Certificate2 certificate = GetCertificate(certificateName);

			string base64DecryptedContent = Convert.ToBase64String(Encoding.UTF8.GetBytes(fullMessage));
			base64DecryptedContent = ChunkContent(base64DecryptedContent, 76);
			base64DecryptedContent = EnvelopeBase64(base64DecryptedContent);

			byte[] contentBytes = Encoding.ASCII.GetBytes(base64DecryptedContent);

			Oid contentOid = new Oid("1.2.840.113549.1.7.1", "PKCS 7 Data");
			Oid algorithmOid = new Oid("1.2.840.113549.3.2", "rc2");
			AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(algorithmOid);
			ContentInfo content = new ContentInfo(contentOid, contentBytes);
			EnvelopedCms envelope = new EnvelopedCms(SubjectIdentifierType.NoSignature, content, algorithmIdentifier);

			envelope.Encrypt(new CmsRecipient(certificate));
			byte[] encryptedBytes = envelope.Encode();

			string encryptedContent = Convert.ToBase64String(encryptedBytes);

			encryptedContent = ChunkContent(encryptedContent, 76);
			string result = EnvelopEncryptedContent(encryptedContent);

			return result;
		}

		public static string Decrypt(string fullMessage)
		{
			string messageContent = GetContentInBase64(fullMessage);

			// Load envelope and decrypt
			EnvelopedCms envelope = new EnvelopedCms();
			envelope.Decode(Convert.FromBase64String(messageContent));
			envelope.Decrypt();

			// Get original bytes
			byte[] decryptedBytes = envelope.ContentInfo.Content;
			string decryptedText = Encoding.ASCII.GetString(decryptedBytes);

			// Get processed Base64 content
			byte[] decryptedContentBytes = Convert.FromBase64String(GetContentInBase64(decryptedText));
			string decryptedContentText = Encoding.UTF8.GetString(decryptedContentBytes);

			return decryptedContentText;
		}
		#endregion

		#region Private Methods
		private static string ChunkContent(string encryptedContent, int chunkSize)
		{
			StringBuilder sb = new StringBuilder();
			StringReader sr = new StringReader(encryptedContent);

			int position = 0;
			char[] buffer = new char[chunkSize];

			while (position < encryptedContent.Length)
			{
				if (encryptedContent.Length - (position + chunkSize) < 0)
				chunkSize = encryptedContent.Length - position;
				sb.Append(encryptedContent.Substring(position, chunkSize));
				sb.Append("rn");
				position += chunkSize;
			}

			return sb.ToString();
		}

		private static string EnvelopEncryptedContent(string encryptedContent)
		{
			return CryptographyResources.ENCRYPTED_TEMPLATE.Replace("[REPLACE]", encryptedContent);
		}

		private static string EnvelopeBase64(string content)
		{
			return CryptographyResources.BASE64_TEMPLATE.Replace("[REPLACE]", content);
		}

		private static X509Certificate2 GetCertificate(string certificateName)
		{
			X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
			store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
			X509Certificate2 certificate = store.Certificates.Cast<X509Certificate2>().Where(cert => cert.Subject.IndexOf(certificateName) >= 0).FirstOrDefault();
			if (certificate == null)
			throw new Exception("Certificate " + certificateName + " not found.");

			return certificate;
		}

		private static string GetContentInBase64(string fullMessage)
		{
			string contentSeparator = Environment.NewLine + Environment.NewLine;
			int startIndex = fullMessage.IndexOf(contentSeparator) + contentSeparator.Length;
			int endIndex = fullMessage.Length - 1;
			StringBuilder sb = new StringBuilder();
			string[] lines = fullMessage.Substring(startIndex, endIndex - startIndex).Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string line in lines)
			sb.Append(line);
			return sb.ToString();
		}
		#endregion
	}
}

Download
Cryptography Helper Source Code (410)

VN:F [1.9.11_1134]
Rating: 5.0/5 (1 vote cast)
Encryption and decryption with X.509 certificates (with MIME Base64 Encoding), 5.0 out of 5 based on 1 rating
Categories: Development Tags: , , ,
  1. peng
    May 20, 2010 5:12 AM | #1

    hey,

    i have found one more resources at

    ( http://seroter.wordpress.com/2007/03/05/building-a-complete-certificate-scenario-with-biztalk-server-2006/ ), which uses out-of-box BTS functionality.

    or is there any more aspects from your approach that I missed?

    br
    Peng

    VA:F [1.9.11_1134]
    Rating: 0 (from 0 votes)
    • May 20, 2010 11:48 AM | #2

      Yes, that’s the normal way to do it if you are only concerned about BizTalk in 32-bit.

      But the idea of this code is to provide a simple way to do the same and be able to:
      1) Use the code in a 64-bit BizTalk host (the standard MIME/SMIME decoder/encoder doesn’t work in 64-bit)
      2) Create unit tests that send/receive encrypted messages to BizTalk and validate that the data is valid.

      Let me know if I can further help you,
      Leandro.

      VN:F [1.9.11_1134]
      Rating: 0 (from 0 votes)
  2. Bunty
    June 10, 2010 8:38 PM | #3

    Hi,

    Thanks for this helpful post. I tried to download your sample code but it is giving file not found error. I tried to compile your but it is failing at line

    return CryptographyResources.ENCRYPTED_TEMPLATE.Replace(“[REPLACE]“, encryptedContent);

    It looks CryptographyHelper is dependent on CryptographyResources class which is missing. Can you please provide code for CryptographyResources class.

    Thanks,

    Bunty

    VA:F [1.9.11_1134]
    Rating: 0 (from 0 votes)
  3. Norbert
    October 28, 2010 5:13 AM | #4

    Hi,

    Nice article! Made me able to replace my old code, which used P/Invoke on crypt32.dll :/

    Thanks

    VA:F [1.9.11_1134]
    Rating: 0 (from 0 votes)
  4. Sam
    March 13, 2011 8:38 AM | #5

    Hi Leandro,

    Thanks alot for this, I was using Capicom dll and it was not working on 64 bit machine. When I deployed the signing process using your code, I communicate with another party who have the public key of my certificate generated from my system. The system works in test environment using capicom, but when we tried to communicate using the prod en 64 bit machine. They get an error when I send them my signed message. Any ideas?

    An error occurred attempting to reconstruct the signature: Error parsing digest algorithms!

    VA:F [1.9.11_1134]
    Rating: 0 (from 0 votes)
  1. No trackbacks yet.