// Copyright (C) Intel Corporation, 2010 All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; /* Decodes and Encodes ASN.1 DER public/private keys */ namespace Intel.Management.Mei { /// /// KeyFomatter formats Public/Private RSA ASN.1 Encoded keys. /// Supports: Reading and writting KeySizes of 1024, 1536, and 2048 /// Supports: Writting keys in AMT legacy format /// Supports: reading and writing AMT legacy certificate chains /// public class KeyFormatter { private const string _keyError = "Invalid or corrupt key file."; private KeyFormatter() { } /// /// read ASN.1 encoded field /// /// ASN.1 input stream /// ANS1 encoded value private byte[] ReadValue(BinaryReader reader) { byte[] result = null; int[] keySizes = { 512, 256, 128, 192, 96, 64 }; byte lowByte = 0x00; byte highByte = 0x00; byte baseByte = 0x00; baseByte = reader.ReadByte(); if (baseByte != 0x02) { throw new ApplicationException("invalid key"); } baseByte = reader.ReadByte(); // size switch (baseByte) { case 0x81: lowByte = reader.ReadByte(); break; case 0x82: highByte = reader.ReadByte(); lowByte = reader.ReadByte(); break; default: lowByte = baseByte; break; } byte[] dataSizeArray = { lowByte, highByte, 0x0, 0x0 }; //reverse byte order since asn.1 key uses big endian int dataSize = BitConverter.ToInt32(dataSizeArray, 0); // check for padding if (reader.PeekChar() == 0x00) { bool padded = true; foreach (int keySize in keySizes) { if (dataSize == keySize) padded = false; } //don't include if its zero (highest order) if (padded) { reader.ReadByte(); //skip this null byte dataSize--; } } result = reader.ReadBytes(dataSize); //read the modulus bytes return result; } private void WriteValue(BinaryWriter writer, byte[] value) { if (value != null) { writer.Write((byte)2); //univernal value int len = value.Length; bool pad = (value[0] & 0x80) > 0; if (pad) len++; int sizeType = 0; if (len < 0x80) sizeType = 0x80; else if (len <= 0xFF) sizeType = 0x81; else if (len <= 0xFFFF) sizeType = 0x82; switch (sizeType) { case 0x81: writer.Write((byte)sizeType); writer.Write((byte)len); break; case 0x82: { writer.Write((byte)sizeType); byte[] dataLen = BitConverter.GetBytes(len); Array.Reverse(dataLen, 0, 2); writer.Write(dataLen, 0, 2); } break; default: writer.Write((byte)len); break; } if (pad) writer.Write((byte)0); writer.Write(value, 0, value.Length); } } private byte[] EncodeKey(RSAParameters rp) { byte[] result; using (MemoryStream memStream = new MemoryStream()) { BinaryWriter writer = new BinaryWriter(memStream, Encoding.ASCII); //write header and size if (rp.P == null && rp.Modulus.Length < 256) // writing public key { writer.Write((ushort)0x8130);// X509 Public Key writer.Write((byte)0);//1 byte size excluding 4 byte header } else { writer.Write((ushort)0x8230); // Private Key writer.Write((ushort)0);//2 byte size excluding 4 byte header } if (rp.P != null) { writer.Write((byte)2); // tag writer.Write((byte)1); // size writer.Write((byte)0);// version 0 } WriteValue(writer, rp.Modulus); WriteValue(writer, rp.Exponent); //public exponent if (rp.P != null) { WriteValue(writer, rp.D); //private exponent WriteValue(writer, rp.P); // prime1 WriteValue(writer, rp.Q); // prime2 WriteValue(writer, rp.DP); //exponent1 WriteValue(writer, rp.DQ); // exponent2 WriteValue(writer, rp.InverseQ);//coefficent } result = new byte[memStream.Length]; Array.Copy(memStream.GetBuffer(), result, result.Length); // copy key size if (rp.P != null) { byte[] dataLen = BitConverter.GetBytes(result.Length - 4); Array.Reverse(dataLen, 0, 2); Array.Copy(dataLen, 0, result, 2, 2); } else { result[2] = (byte)(result.Length - 3); } } return result; } private RSACryptoServiceProvider GetKey(byte[] rsakey) { byte[] modulus, exponent; byte[] D = null, P = null, Q = null; byte[] DP = null, DQ = null, IQ = null; int keySize = 0; // asn.1 encoded Key blob using(MemoryStream memStream = new MemoryStream(rsakey)) { BinaryReader reader = new BinaryReader(memStream, Encoding.ASCII); ushort data16 = 0; byte b8 = 0x00; data16 = reader.ReadUInt16(); if (data16 == 0x8130) // (really 30 81)little endian reader.ReadByte(); //advance 1 byte else if (data16 == 0x8230) //( really 30 82) reader.ReadUInt16(); //advance 2 bytes (stream size) else throw new ApplicationException("invalid key"); //read 1 and 0 if (data16 == 0x8230) { // small key (public only) ///reader.BaseStream. b8 = reader.ReadByte(); if (b8 != 0x02) throw new ApplicationException("invalid key"); // check private key header version header data16 = reader.ReadUInt16(); if (data16 != 1) reader.BaseStream.Position -= 3; } modulus = ReadValue(reader);//81 padded exponent = ReadValue(reader); // private key file if (reader.PeekChar() != -1) { D = ReadValue(reader);//81 P = ReadValue(reader);//41 padded Q = ReadValue(reader);//41 padded DP = ReadValue(reader);//40 DQ = ReadValue(reader);//41 padded IQ = ReadValue(reader);//40 } if (reader.PeekChar() != -1) { throw new ApplicationException("invalid key"); } } RSAParameters rp = new RSAParameters(); rp.Modulus = modulus; rp.Exponent = exponent; rp.D = D; rp.P = P; rp.Q = Q; rp.DP = DP; rp.DQ = DQ; rp.InverseQ = IQ; if (rp.Modulus.Length == 512) keySize = 4096; if (rp.Modulus.Length == 256) keySize = 2048; else if (rp.Modulus.Length == 192) keySize = 1536; else if (rp.Modulus.Length == 128) keySize = 1024; else throw new ApplicationException("invalid key"); RSACryptoServiceProvider result; using (result = new RSACryptoServiceProvider(keySize)) { result.ImportParameters(rp); } return result; } private void WriteKeyParam(MemoryStream memStream, byte[] data, bool pad) { // write the size of the param in 4 byte network order int len = data.Length; if (pad) len++; byte[] sizeBuffer = BitConverter.GetBytes(len); Array.Reverse(sizeBuffer); memStream.Write(sizeBuffer, 0, sizeBuffer.Length); if (pad) memStream.WriteByte(0); //Array.Reverse(newData); memStream.Write(data, 0, data.Length); } private byte[] EncodeAMTKey(RSAParameters rp) { //bool pad = false; byte[] result; using (MemoryStream stream = new MemoryStream()) { WriteKeyParam(stream, rp.P, true); WriteKeyParam(stream, rp.Q, true); WriteKeyParam(stream, rp.Modulus, true); //don't pad exponent WriteKeyParam(stream, rp.Exponent, false); result = new byte[stream.Length]; Array.Copy(stream.GetBuffer(), result, result.Length); } return result; } public static X509Certificate[] GetCertChain(byte[] certChain) { X509Certificate[] certs; using (MemoryStream stream = new MemoryStream(certChain)) { List list = new List(); while (true) { byte[] data = { 0, 0, 0, 0 }; int br = stream.Read(data, 1, 3); if (br != 3) break; Array.Reverse(data); int certSize = BitConverter.ToInt32(data, 0); data = new byte[certSize]; br = stream.Read(data, 0, certSize); if (br != certSize) break; list.Add(new X509Certificate(data)); } certs = new X509Certificate[list.Count]; list.CopyTo(certs); } return certs; } public static byte[] GetCertChain(IList list) { X509Certificate[] array = new X509Certificate[list.Count]; list.CopyTo(array, 0); return GetCertChain(array); } public static byte[] GetCertChain(X509Certificate[] certs) { byte[] result; using (MemoryStream stream = new MemoryStream()) { foreach (X509Certificate cert in certs) { byte[] data = cert.GetRawCertData(); byte[] bufSize = BitConverter.GetBytes(data.Length); Array.Reverse(bufSize); stream.Write(bufSize, 1, 3); stream.Write(data, 0, data.Length); } result = new byte[stream.Length]; Array.Copy(stream.GetBuffer(), result, result.Length); } return result; } public static byte[] EncodeKey(RSACryptoServiceProvider rsa, bool doPrivate) { KeyFormatter formatter = new KeyFormatter(); RSAParameters rp = rsa.ExportParameters(doPrivate); return formatter.EncodeKey(rp); } public static byte[] EncodeAMTKey(RSACryptoServiceProvider rsa) { KeyFormatter formatter = new KeyFormatter(); RSAParameters rp = rsa.ExportParameters(true); return formatter.EncodeAMTKey(rp); } public static RSACryptoServiceProvider GetRSAKey(byte[] data) { KeyFormatter formatter = new KeyFormatter(); RSACryptoServiceProvider result = formatter.GetKey(data); return result; } } }