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.
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)
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
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.