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

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

March 29th, 2010 Logue Leave a comment Go to comments 765 views

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.

Full source code and download for the CryptographyHelper ahead.

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("\r\n");
                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 (downloaded 7 times)

Categories: Development Tags: , , ,
  1. peng
    May 20th, 2010 at 05:12 | #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

    • May 20th, 2010 at 11:48 | #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.

  1. No trackbacks yet.