using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Security.Cryptography;
class AsnKeyParser
{
private AsnParser parser;
internal AsnKeyParser(String pathname)
{
using (BinaryReader reader = new BinaryReader(
new FileStream(pathname, FileMode.Open, FileAccess.Read)))
{
FileInfo info = new FileInfo(pathname);
parser = new AsnParser(reader.ReadBytes((int)info.Length));
}
}
internal byte[] TrimLeadingZero(byte[] values)
{
byte[] r = null;
if ( (0x00 == values[0]) && (values.Length > 1) )
{
r = new byte[values.Length - 1];
Array.Copy(values, 1, r, 0, values.Length - 1);
}
else
{
r = new byte[values.Length];
Array.Copy(values, r, values.Length);
}
return r;
}
internal static bool EqualOid(byte[] first, byte[] second)
{
if (first.Length != second.Length)
{ return false; }
for (int i = 0; i < first.Length; i++)
{
if (first[i] != second[i])
{ return false; }
}
return true;
}
internal RSAParameters ParseRSAPublicKey()
{
RSAParameters parameters = new RSAParameters();
// Current value
byte[] value = null;
// Current Position
int position = parser.CurrentPosition();
// Ignore Sequence - PublicKeyInfo
parser.NextSequence();
// Ignore Sequence - AlgorithmIdentifier
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Grab the OID
value = parser.NextOID();
byte[] oid = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
if (!EqualOid(value, oid))
{ throw new BERDecodeException("Expected OID 1.2.840.113549.1.1.1", position); }
// Optional Parameters
if (parser.IsNextNull())
{
parser.NextNull();
}
else
{
// Gracefully skip the optional data
// hopefully, it is a sequence
int size = parser.NextSequence();
value = parser.GetOctets(size);
}
// Ignore BitString - PublicKey
parser.NextBitString();
// Ignore Sequence - RSAPublicKey
parser.NextSequence();
parameters.Modulus = TrimLeadingZero(parser.NextInteger());
parameters.Exponent = TrimLeadingZero(parser.NextInteger());
Debug.Assert(0 == parser.RemainingBytes());
return parameters;
}
internal RSAParameters ParseRSAPrivateKey()
{
RSAParameters parameters = new RSAParameters();
// Current value
byte[] value = null;
// Current position
int position = parser.CurrentPosition();
// Ignore Sequence - PrivateKeyInfo
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Version
value = parser.NextInteger();
if (0x00 != value[0])
{
throw new BERDecodeException("Incorrect PrivateKeyInfo Version", position);
}
// Ignore Sequence - AlgorithmIdentifier
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Grab the OID
value = parser.NextOID();
byte[] oid = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
if (!EqualOid(value, oid))
{ throw new BERDecodeException("Expected OID 1.2.840.113549.1.1.1", position); }
// Optional Parameters
if (parser.IsNextNull())
{
parser.NextNull();
}
else
{
// Gracefully skip the optional data
// hopefully, it is a sequence
int size = parser.NextSequence();
value = parser.GetOctets(size);
}
// Ignore OctetString - PrivateKey
parser.NextOctetString();
// Ignore Sequence - RSAPrivateKey
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Version
value = parser.NextInteger();
if (0x00 != value[0])
{
throw new BERDecodeException("Incorrect RSAPrivateKey Version", position);
}
parameters.Modulus = TrimLeadingZero(parser.NextInteger());
parameters.Exponent = TrimLeadingZero(parser.NextInteger());
parameters.D = TrimLeadingZero(parser.NextInteger());
parameters.P = TrimLeadingZero(parser.NextInteger());
parameters.Q = TrimLeadingZero(parser.NextInteger());
parameters.DP = TrimLeadingZero(parser.NextInteger());
parameters.DQ = TrimLeadingZero(parser.NextInteger());
parameters.InverseQ = TrimLeadingZero(parser.NextInteger());
Debug.Assert(0 == parser.RemainingBytes());
return parameters;
}
internal DSAParameters ParseDSAPublicKey()
{
DSAParameters parameters = new DSAParameters();
// Current value
byte[] value = null;
// Current Position
int position = parser.CurrentPosition();
// Ignore Sequence - PublicKeyInfo
parser.NextSequence();
// Ignore Sequence - AlgorithmIdentifier
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Grab the OID
value = parser.NextOID();
byte[] oid = { 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01 };
if (!EqualOid(value, oid))
{ throw new BERDecodeException("Expected OID 1.2.840.10040.4.1", position); }
// Ignore Sequence - DSS-Params
parser.NextSequence();
// Next three are curve parameters
parameters.P = TrimLeadingZero(parser.NextInteger());
parameters.Q = TrimLeadingZero(parser.NextInteger());
parameters.G = TrimLeadingZero(parser.NextInteger());
// Ignore BitString - PrivateKey
parser.NextBitString();
// Public Key
parameters.Y = TrimLeadingZero(parser.NextInteger());
Debug.Assert(0 == parser.RemainingBytes());
return parameters;
}
internal DSAParameters ParseDSAPrivateKey()
{
DSAParameters parameters = new DSAParameters();
// Current value
byte[] value = null;
// Current Position
int position = parser.CurrentPosition();
// Ignore Sequence - PrivateKeyInfo
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Version
value = parser.NextInteger();
if (0x00 != value[0])
{
throw new BERDecodeException("Incorrect PrivateKeyInfo Version", position);
}
// Ignore Sequence - AlgorithmIdentifier
parser.NextSequence();
// Current position
position = parser.CurrentPosition();
// Grab the OID
value = parser.NextOID();
byte[] oid = { 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01 };
if (!EqualOid(value, oid))
{ throw new BERDecodeException("Expected OID 1.2.840.10040.4.1", position); }
// Ignore Sequence - DSS-Params
parser.NextSequence();
// Next three are curve parameters
parameters.P = TrimLeadingZero(parser.NextInteger());
parameters.Q = TrimLeadingZero(parser.NextInteger());
parameters.G = TrimLeadingZero(parser.NextInteger());
// Ignore OctetString - PrivateKey
parser.NextOctetString();
// Private Key
parameters.X = TrimLeadingZero(parser.NextInteger());
Debug.Assert(0 == parser.RemainingBytes());
return parameters;
}
}
class AsnParser
{
private List<byte> octets;
private int initialCount;
internal AsnParser(byte[] values)
{
octets = new List<byte>(values.Length);
octets.AddRange(values);
initialCount = values.Length;
}
internal int CurrentPosition()
{
return initialCount - octets.Count;
}
internal int RemainingBytes()
{
return octets.Count;
}
private int GetLength()
{
int length = 0;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (b < 0x80) { return b; }
int i = b & 0x7f;
while (0 != i--)
{
// shift left
length <<= 8;
length |= octets[0];
octets.RemoveAt(0);
}
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - 1, ex); }
return length;
}
internal byte[] Next()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
int length = GetLength();
return null;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal byte[] GetOctets(int octetCount)
{
int count = octets.Count;
byte[] values = new byte[octetCount];
try
{
for (int i = 0; i < octetCount; i++)
{
values[i] = octets[0];
octets.RemoveAt(0);
}
return values;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal bool IsNextNull()
{
return 0x05 == octets[0];
}
internal int NextNull()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (0x05 != b)
{ throw new BERDecodeException("Expected Null", initialCount - count); }
// Next octet must be 0
octets.RemoveAt(0);
return 0;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal bool IsNextSequence()
{
return 0x30 == octets[0];
}
internal int NextSequence()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (0x30 != b)
{ throw new BERDecodeException("Expected Sequence", initialCount - count); }
int length = GetLength();
return length;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal bool IsNextOctetString()
{
return 0x04 == octets[0];
}
internal int NextOctetString()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (0x04 != b)
{ throw new BERDecodeException("Expected OctetString", initialCount - count); }
int length = GetLength();
return length;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal bool IsNextBitString()
{
return 0x03 == octets[0];
}
internal int NextBitString()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (0x03 != b)
{ throw new BERDecodeException("Expected BitString", initialCount - count); }
int length = GetLength();
// We need to consume unused bits
b = octets[0];
octets.RemoveAt(0);
if (0x00 != b)
{ throw new BERDecodeException("Null must have a zero length", initialCount - count); }
return length;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal bool IsNextInteger()
{
return 0x02 == octets[0];
}
internal byte[] NextInteger()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (0x02 != b)
{ throw new BERDecodeException("Expected Integer", initialCount - count); }
int length = GetLength();
byte[] values = new byte[length];
for (int i = 0; i < length; i++)
{
values[i] = octets[0];
octets.RemoveAt(0);
}
return values;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
internal byte[] NextOID()
{
int count = octets.Count;
try
{
byte b = octets[0];
octets.RemoveAt(0);
if (0x06 != b)
{ throw new BERDecodeException("Expected ObjectIdentifier", initialCount - count); }
int length = GetLength();
byte[] values = new byte[length];
for (int i = 0; i < length; i++)
{
values[i] = octets[0];
octets.RemoveAt(0);
}
return values;
}
catch (ArgumentOutOfRangeException ex)
{ throw new BERDecodeException(initialCount - count, ex); }
}
}