如何将BER编码与对象System.DirectoryServices.Protocols.BerConverter.Encode(“???”,myData)一起使用

时间:2012-06-29 21:47:19

标签: encoding character-encoding ldap x509certificate directoryservices

我需要对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,证书,并且在许多其他格式中很常见。

我会很高兴收到有关如何对此课程进行编码或解码的信息。 Stack Overflow或Google(或Bing)的前几页没有任何内容。

问题

  • 如何使用BER解码将上面的字节数组转换为相应的OID?

  • 如何以DER或BER格式解析(或尝试解析)SubjectPublicKeyInfo ASN.1数据?

  • 似乎DER编码\解码类是.NET框架的内部。如果是这样,他们在哪里? (我想请connect.microsoft.com公开这些成员)

1 个答案:

答案 0 :(得分:5)

  

如何使用BER解码将上面的字节数组转换为相应的OID?

解压缩OID字节数组后,可以使用OidByteArrayToString()将其转换为OID字符串。我已经包含了下面的代码,因为我在.NET库中找不到类似的功能。

  

如何以DER或BER格式解析(或尝试解析)SubjectPublicKeyInfo ASN.1数据?

我也无法在.NET SDK中找到TLV解析器。以下是BER TLV解析器BerTlv的实现。由于DER是BER的子集,因此解析将以相同的方式工作。给定BER-TLV byte[]数组,它将返回支持子TLV访问的BerTlv个对象列表。

  

似乎DER编码\解码类是.NET框架的内部。如果是这样,他们在哪里? (我想请connect.microsoft.com让这些成员公开)

也许其他人可以回答这个问题。

<强>摘要

以下是如何使用下面提供的代码的示例。我使用了您之前post中提供的公开密钥数据。应该增加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编码/解码

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;
    }
}