Friday, February 26, 2010

Encrypting/Decrypting using DigitalCertificates using C#

Here is some quick code I wrote up that allows you to perform Asymmetric encryption using the RSA algorithm. The keys used are from a digital certificate stored in the local user’s cert store (the code to create a certificate for testing is also included in the sample.

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

namespace X509Certificate2_Encryption_Example
{
    class X509Certificate2_Cryptographer
    {
        //creating the certificate
        //makecert -r -pe -n "CN=WWW.AGGREGATEDINTELLIGENCE.COM" -b 01/01/2005 -e 01/01/2020 -sky exchange -ss my
        public static void Main()
        {
            try
            {
                X509Certificate2 x509_2 = LoadCertificate(StoreLocation.CurrentUser, "CN=WWW.AGGREGATEDINTELLIGENCE.COM");
                //DisplayX509CertificateInfo(x509_2);
                
                string plaintext = "HelloWorld";
                Console.WriteLine("Plain text: " + plaintext + Environment.NewLine);
                
                string encryptedstring = Encrypt(x509_2, plaintext);
                Console.WriteLine("Encrypted text: " + Environment.NewLine + encryptedstring + Environment.NewLine);
                
                string decryptedstring = Decrypt(x509_2, encryptedstring);
                Console.WriteLine("decrypted text: " + decryptedstring + Environment.NewLine);                
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
            Console.ReadLine();
        }
        
        public static X509Certificate2 LoadCertificate(StoreLocation storeLocation, string certificateName )
        {
            X509Store store = new X509Store(storeLocation);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certCollection = store.Certificates;
            X509Certificate2 x509 = null;
            foreach (X509Certificate2 c in certCollection)
            {
                if (c.Subject == certificateName)
                {
                    x509 = c;
                    break;
                }
            }
            if (x509 == null)
                Console.WriteLine("A x509 certificate for " + certificateName + " was not found");
            store.Close();
            return x509;
        }
        
        public static void DisplayX509CertificateInfo(X509Certificate2 x509)
        {
            if (x509 == null)
                throw new Exception("A x509 certificate must be provided");
                
            Console.WriteLine("{0}Subject: {1}{0}", Environment.NewLine,x509.Subject);
            Console.WriteLine("{0}Issuer: {1}{0}", Environment.NewLine,x509.Issuer);
            Console.WriteLine("{0}Version: {1}{0}", Environment.NewLine,x509.Version);
            Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine,x509.NotBefore);
            Console.WriteLine("{0}Expiry Date: {1}{0}", Environment.NewLine,x509.NotAfter);
            Console.WriteLine("{0}Thumbprint: {1}{0}", Environment.NewLine,x509.Thumbprint);
            Console.WriteLine("{0}Serial Number: {1}{0}", Environment.NewLine,x509.SerialNumber);
            Console.WriteLine("{0}Friendly Name: {1}{0}",Environment.NewLine,x509.PublicKey.Oid.FriendlyName);
            Console.WriteLine("{0}Public Key Format: {1}{0}",Environment.NewLine,x509.PublicKey.EncodedKeyValue.Format(true));
            Console.WriteLine("{0}Raw Data Length: {1}{0}", Environment.NewLine,x509.RawData.Length);
            Console.WriteLine("{0}Certificate to string: {1}{0}", Environment.NewLine,x509.ToString(true));

            Console.WriteLine("{0}Certificate to XML String: {1}{0}",Environment.NewLine,x509.PublicKey.Key.ToXmlString(false));
        }
        
        public static string Encrypt(X509Certificate2 x509, string stringToEncrypt)
        {
            if (x509 == null || string.IsNullOrEmpty(stringToEncrypt))
                throw new Exception("A x509 certificate and string for encryption must be provided");
                
            RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509.PublicKey.Key;
            byte[] bytestoEncrypt = ASCIIEncoding.ASCII.GetBytes(stringToEncrypt);
            byte[] encryptedBytes = rsa.Encrypt(bytestoEncrypt, false);
            return Convert.ToBase64String(encryptedBytes);
        }
        
        public static string Decrypt(X509Certificate2 x509, string stringTodecrypt)
        {
            if (x509 == null || string.IsNullOrEmpty(stringTodecrypt))
                throw new Exception("A x509 certificate and string for decryption must be provided");
                
            if (!x509.HasPrivateKey)
                throw new Exception("x509 certicate does not contain a private key for decryption");
                
            RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509.PrivateKey;
            byte[] bytestodecrypt = Convert.FromBase64String(stringTodecrypt);
            byte[] plainbytes = rsa.Decrypt(bytestodecrypt, false);
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            return enc.GetString(plainbytes);
        }
    }
}

Notes:

RSACryptoServiceProvider: http://msdn.microsoft.com/en-us/library/bfsktky3(VS.80).aspx

MakeCert: http://msdn.microsoft.com/en-us/library/bfsktky3(VS.80).aspx

X509Certificate2 Class: http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx

1 comment:

Dan said...

I've played with this some before. Yes, this works just great in a console or Windows application. The problem comes in when you want to read those X509 certs over a website. It works just great using the web server within VS, but I have not had any luck with IIS. Do you have any code samples or configuration examples for a published web environment? Thanks!