Archive

Posts Tagged ‘C#’

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

March 29, 2010 LeandroDG and adglopez 6 comments

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 (476)

VN:F [1.9.11_1134]
Rating: 5.0/5 (1 vote cast)
Categories: Development Tags: , , ,

.NET C#: Recycle current Application Pool programmatically (for IIS 6+)

February 12, 2008 LeandroDG 7 comments

I’ve been working on how to recycle the current application pool for my ASP .NET application.

There are 3 steps for doing this:

  1. Verify if application is running on IIS that supports application pools (if not, there’s nothing to recycle).
  2. Get application pool name (obtained from the DirectoryServices entry corresponding to our virtual directory).
  3. Invoke Recycle method in the DirectoryServices entry corresponding to the application pool.

I divided the code into this 3 steps, the RecycleApplicationPool method can be used separately for recycling any application pool (by knowing only its name). The RecycleCurrentApplicationPool method returns a boolean value indicating if the application pool was recycled.

public static class ApplicationPoolRecycle
{
    /// <summary>Attempts to recycle current application pool</summary>
    /// <returns>Boolean indicating if application pool was successfully recycled</returns>
    public static bool RecycleCurrentApplicationPool()
    {
        try
        {
            // Application hosted on IIS that supports App Pools, like 6.0 and 7.0
            if (IsApplicationRunningOnAppPool())
            {
                // Get current application pool name
                string appPoolId = GetCurrentApplicationPoolId();
                // Recycle current application pool
                RecycleApplicationPool(appPoolId);
                return true;
            }
            else
                return false;
        }
        catch
        {
            return false;
        }
    }

    private static bool IsApplicationRunningOnAppPool()
    {
        // Application is not hosted on IIS
        if (!AppDomain.CurrentDomain.FriendlyName.StartsWith("/LM/"))
            return false;
        // Application hosted on IIS that doesn't support App Pools, like 5.1
        else if (!DirectoryEntry.Exists("IIS://Localhost/W3SVC/AppPools"))
            return false;
        else
            return true;
    }

    private static string GetCurrentApplicationPoolId()
    {
        string virtualDirPath = AppDomain.CurrentDomain.FriendlyName;
        virtualDirPath = virtualDirPath.Substring(4);
        int index = virtualDirPath.Length + 1;
        index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
        index = virtualDirPath.LastIndexOf("-", index - 1, index - 1);
        virtualDirPath = "IIS://localhost/" + virtualDirPath.Remove(index);
        DirectoryEntry virtualDirEntry = new DirectoryEntry(virtualDirPath);
        return virtualDirEntry.Properties["AppPoolId"].Value.ToString();
    }

    private static void RecycleApplicationPool(string appPoolId)
    {
        string appPoolPath = "IIS://localhost/W3SVC/AppPools/" + appPoolId;
        DirectoryEntry appPoolEntry = new DirectoryEntry(appPoolPath);
        appPoolEntry.Invoke("Recycle");
    }
}

VN:F [1.9.11_1134]
Rating: 5.0/5 (1 vote cast)

Determine current execution context (ASP.NET or Winforms)

October 5, 2007 LeandroDG 2 comments

I’ve searched over the internet for this and couldn’t find anything.

A method I’m working on should have a different behavior when called in a Web Application (Webforms) and a Windows Application (Winforms).

This is what I came up with, using Reflection:

	if (Assembly.GetEntryAssembly() != null)
	{
		// Running on Winforms context
	}
	else
	{
		// Running on ASP.NET context
	}

There are probably other ways to do it, I’ve tested this one and it works great.

Update (2007-16-10):

Turns out there is an easier way to do this, just got it from a co-worker. Instead of Assembly.GetEntryAssembly() you can use HttpContext.Current, but you’ll have to be careful with threading in ASP .NET forms, as seen in http://www.odetocode.com/Articles/112.aspx (HttpContext.Current could be null while running on ASP .NET).

VN:F [1.9.11_1134]
Rating: 0.0/5 (0 votes cast)