442 lines
13 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
}
|