Вопрос по x509certificate, ldap, encoding, directoryservices, character-encoding – Как использовать кодирование BER с объектом System.DirectoryServices.Protocols.BerConverter.Encode («???», myData)

2

Мне нужно кодировать и декодировать данные BER. .NET имеет классSystem.DirectoryServices.Protocols.BerConverter

Статический метод требует от меня ввести строку в первый параметр, как показано ниже

        byte[] oid = { 0x30, 0xD, 0x6, 0x9, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0 }; // Object ID for RSA

        var result2 = System.DirectoryServices.Protocols.BerConverter.Decoding("?what goes here?", oid);

Кодировка BER используется в LDAP, Сертификатах и широко распространена во многих других форматах.

Я буду рад информации, рассказывающей мне, как кодировать или декодировать этот класс. Нет ничего о переполнении стека или первых нескольких страницах Google (или Bing) по этому поводу.

Question

How do I convert the byte array above to the corresponding OID using BER decoding?

How can I parse (or attempt to parse) SubjectPublicKeyInfo ASN.1 data in DER or BER format?

It seems the DER encoding\decoding classes are internal to the .NET framework. If so, where are they? (I'd like to ask connect.microsoft.com to make these members public)

Если этот вопрос остается без ответа, я предлагаю парсер TLV общего назначения. Вышеприведенная функция кажется очень специфичной для DirectoryServices. user845279

Ваш Ответ

1   ответ
5

How do I convert the byte array above to the corresponding OID using BER decoding?

После того, как вы извлекли байтовый массив OID, вы можете преобразовать его в строку OID, используяOidByteArrayToString(), Я включил приведенный ниже код, поскольку не смог найти подобную функцию в библиотеках .NET.

How can I parse (or attempt to parse) SubjectPublicKeyInfo ASN.1 data in DER or BER format?

Я не смог найти анализатор TLV в .NET SDK. Ниже приведена реализация синтаксического анализатора BER TLV,BerTlv, Поскольку DER является подмножеством BER, синтаксический анализ будет работать таким же образом. Учитывая BER-TLVbyte[] массив, он вернет списокBerTlv объекты, которые поддерживают доступ к подчиненным TLV.

It seems the DER encoding\decoding classes are internal to the .NET framework. If so, where are they? (I'd like to ask connect.microsoft.com to make these members public)

Может быть, кто-то еще может ответить на этот вопрос.

Summary

Вот пример того, как вы можете использовать приведенный ниже код. Я использовал данные открытых ключей, которые вы указали в предыдущемсообщение, BerTlv, вероятно, должен быть расширен для поддержки запросов, таких какBerTlv.getValue(rootTlvs, '/30/30/06');.

public static void Main(string[] args)
{
    string pubkey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrEee0Ri4Juz+QfiWYui/E9UGSXau/2P8LjnTD8V4Unn+2FAZVGE3kL23bzeoULYv4PeleB3gfmJiDJOKU3Ns5L4KJAUUHjFwDebt0NP+sBK0VKeTATL2Yr/S3bT/xhy+1xtj4RkdV7fVxTn56Lb4udUnwuxK4V5b5PdOKj/+XcwIDAQAB";
    byte[] pubkeyByteArray = Convert.FromBase64String(pubkey);
    List<BerTlv> rootTlvs = BerTlv.parseTlv(pubkeyByteArray);

    BerTlv firstTlv = rootTlvs.Where(tlv => tlv.Tag == 0x30).First();//first sequence (tag 30)
    BerTlv secondTlv = firstTlv.SubTlv.Where(tlv => tlv.Tag == 0x30).First();//second sequence (tag 30)
    BerTlv oid = secondTlv.SubTlv.Where(tlv => tlv.Tag == 0x06).First();//OID tag (tag 30)

    string strOid = OidByteArrayToString(oid.Value);
    Console.WriteLine(strOid);
}

Выход:

1.2.840.113549.1.1.1

OID Encode/Decode

public static byte[] OidStringToByteArray(string oid)
{
    string[] split = oid.Split('.');
    List<byte> retVal = new List<byte>();

    //root arc
    if (split.Length > 0)
        retVal.Add((byte)(Convert.ToInt32(split[0])*40));

    //first arc
    if (split.Length > 1)
        retVal[0] += Convert.ToByte(split[1]);

    //subsequent arcs
    for (int i = 2; i < split.Length; i++)
    {
        int arc_value = Convert.ToInt32(split[i]);
        Stack<byte> bytes = new Stack<byte>();
        while (arc_value != 0)
        {
            byte val = (byte) ((arc_value & 0x7F) | (bytes.Count == 0 ? 0x0:0x80));
            arc_value >>= 7;
            bytes.Push(val);
        }
        retVal.AddRange(bytes);
    }
    return retVal.ToArray();
}

public static string OidByteArrayToString(byte[] oid)
{
    StringBuilder retVal = new StringBuilder();

    //first byte
    if (oid.Length > 0)
        retVal.Append(String.Format("{0}.{1}", oid[0] / 40, oid[0] % 40));

    // subsequent bytes
    int current_arc = 0;
    for (int i = 1; i < oid.Length; i++)
    {
        current_arc = (current_arc <<= 7) | oid[i] & 0x7F;

        //check if last byte of arc value
        if ((oid[i] & 0x80) == 0)
        {
            retVal.Append('.');
            retVal.Append(Convert.ToString(current_arc));
            current_arc = 0;
        }
    }

    return retVal.ToString();
}

BER-TLV Parser

class BerTlv
{
    private int tag;
    private int length;
    private int valueOffset;
    private byte[] rawData;
    private List<BerTlv> subTlv;

    private BerTlv(int tag, int length, int valueOffset, byte[] rawData)
    {
        this.tag = tag;
        this.length = length;
        this.valueOffset = valueOffset;
        this.rawData = rawData;
        this.subTlv = new List<BerTlv>();
    }
    public int Tag
    {
        get { return tag; }
    }
    public byte[] RawData
    {
        get { return rawData; }
    }
    public byte[] Value
    {
        get
        {
            byte[] result = new byte[length];
            Array.Copy(rawData, valueOffset, result, 0, length);
            return result;
        }
    }
    public List<BerTlv> SubTlv
    {
        get { return subTlv; }
    }
    public static List<BerTlv> parseTlv(byte[] rawTlv)
    {
        List<BerTlv> result = new List<BerTlv>();
        parseTlv(rawTlv, result);
        return result;
    }
    private static void parseTlv(byte[] rawTlv, List<BerTlv> result)
    {
        for (int i = 0, start=0; i < rawTlv.Length; start=i)
        {
            //parse Tag
            bool constructed_tlv = (rawTlv[i] & 0x20) != 0;
            bool more_bytes = (rawTlv[i] & 0x1F) == 0x1F;
            while (more_bytes && (rawTlv[++i] & 0x80) != 0) ;
            i++;

            int tag = Util.getInt(rawTlv, start, i-start);

            //parse Length
            bool multiByte_Length = (rawTlv[i] & 0x80) != 0;

            int length = multiByte_Length ? Util.getInt(rawTlv, i+1, rawTlv[i] & 0x1F) : rawTlv[i];
            i = multiByte_Length ? i + (rawTlv[i] & 0x1F) + 1: i + 1;

            i += length;

            byte[] rawData = new byte[i - start];
            Array.Copy(rawTlv, start, rawData, 0, i - start);
            BerTlv tlv = new BerTlv(tag, length, i - length, rawData);
            result.Add(tlv);

            if (constructed_tlv)
                parseTlv(tlv.Value, tlv.subTlv);

        }
    }
}

Вот вспомогательный класс, который содержит некоторые функции, используемые в классе выше. Это включено для ясности, как это работает.

class Util
{
    public static string getHexString(byte[] arr)
    {
        StringBuilder sb = new StringBuilder(arr.Length * 2);
        foreach (byte b in arr)
        {
            sb.AppendFormat("{0:X2}", b);
        }
        return sb.ToString();
    }
    public static byte[] getBytes(String str)
    {
        byte[] result = new byte[str.Length >> 1];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = (byte)Convert.ToInt32(str.Substring(i * 2, 2), 16);
        }
        return result;
    }
    public static int getInt(byte[] data, int offset, int length)
    {
        int result = 0;
        for (int i = 0; i < length; i++)
        {
            result = (result << 8) | data[offset + i];
        }
        return result;
    }
}
Спасибо за этот код разбора. Это спасло мне много работы. Очень лаконично
Спасибо за это. Работает отлично. Для дальнейшего использования я нашел здесь одну ошибку. В вашемBerTlv учебный класс,parseTlv функция,BerTlv tlv = new BerTlv(tag, length, i - length, rawData); должно бытьBerTlv tlv = new BerTlv(tag, length, rawData.Length - length, rawData);.

Похожие вопросы