Convert RSA public key from XML to PEM format (.NET) (Part 1)

Probably the people working with asymmetric cryptography have struggled for a way to convert the XML format of the RSA public key to the more widely used PEM format. Although there is a solution for the reverse transformation (from PEM to XML) on the following address http://www.jensign.com/opensslkey/opensslkey.cs I have not found anywhere a solution to this problem.

So after a bit of reading and examining the code in the above mentioned link I've come up with a small code that does the conversion and the resulting key is parsed OK from OpenSSL.

NOTE: you will need to download and use the assemblies from http://clrsecurity.codeplex.com/

NOTE2: The code bellow only works under Windows 7 and Windows Server 2008 R2, because it uses the Cryptographic Next Generation (CNG) that were added only to those operating systems.

C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            RSACng rsa = new RSACng();
            X509Certificate2 cert;
            List<byte> arrBinaryPublicKey = new List<byte>();

            byte[] oid = 
            { 0x30, 0xD, 0x6, 0x9, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0 }; // Object ID for RSA

            //rsa.FromXmlString(xmlFormatedRSAKey);

            cert = rsa.Key.CreateSelfSignedCertificate(new X500DistinguishedName("CN=something"));

            //Transform the public key to PEM Base64 Format
            arrBinaryPublicKey = cert.PublicKey.EncodedKeyValue.RawData.ToList();
            arrBinaryPublicKey.Insert(0, 0x0); // Add NULL value

            CalculateAndAppendLength(ref arrBinaryPublicKey);

            arrBinaryPublicKey.Insert(0, 0x3);
            arrBinaryPublicKey.InsertRange(0, oid);

            CalculateAndAppendLength(ref arrBinaryPublicKey);

            arrBinaryPublicKey.Insert(0, 0x30);
            //End Transformation

            Console.WriteLine();
            Console.WriteLine("-----BEGIN PUBLIC KEY-----");
            Console.WriteLine(System.Convert.ToBase64String(arrBinaryPublicKey.ToArray()));
            Console.WriteLine("-----END PUBLIC KEY-----");

        }

        private static void CalculateAndAppendLength(ref List<byte> arrBinaryData)
        {
            int nLen;
            nLen = arrBinaryData.Count;
            if (nLen <= byte.MaxValue)
            {
                arrBinaryData.Insert(0, Convert.ToByte(nLen));
                arrBinaryData.Insert(0, 0x81); //This byte means that the length fits in one byte
            }
            else
            {
                arrBinaryData.Insert(0, Convert.ToByte(nLen % (byte.MaxValue + 1)));
                arrBinaryData.Insert(0, Convert.ToByte(nLen / (byte.MaxValue + 1)));
                arrBinaryData.Insert(0, 0x82); //This byte means that the length fits in two byte
            }

        }

    }
}


Compiled source available here

Labels: , , , , ,