442 lines
13 KiB
C#

// 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
{
/// <summary>
/// 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
/// </summary>
public class KeyFormatter
{
private const string _keyError = "Invalid or corrupt key file.";
private KeyFormatter() { }
/// <summary>
/// read ASN.1 encoded field
/// </summary>
/// <param name="reader">ASN.1 input stream</param>
/// <returns>ANS1 encoded value</returns>
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<X509Certificate> list = new List<X509Certificate>();
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<X509Certificate> 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;
}
}
}