Click here to Skip to main content
15,886,519 members
Articles / Desktop Programming / Win32

Cryptographic Interoperability: Digital Signatures

Rate me:
Please Sign up or sign in to vote.
4.98/5 (48 votes)
20 Oct 2009CPOL21 min read 219.7K   12.4K   176  
Sign and verify messages using Crypto++, Java, and C#.
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); }
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Systems / Hardware Administrator
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions