当前位置:   article > 正文

BER-TLV的认识与原子数据解析

ber-tlv

BER-TLV

BER-TLV中BER是Basic Encoding Rules的简写,是ASN.1的一种编码格式,它是将抽象信息编码成为具体数据流的最初规则。

相关知识及定义来自:X.690

BER编码

BER格式描述了自我描述与分界的数据结构。每个数据编码为一个由type标识,length描述,data数据的元素,并且在必要的位置添加结束标识。这个格式可以在不需要提前了解数据的大小,内容,及数据意义情况下允许接收方从数据流(data inputstream)中解码数据。

BER格式

BER元素结构

这是4个部分组成的一个单元组,末尾的八元组(字节)是可选的,只在长度形式未定义情况下使用。内容(Contents octets)部分也可能在为null的情况下被移除。

Tag/Type域

Type

这部分数据(尤其是序列(sequences),集合(sets),选项(choices)的成员)由唯一tag数标识,区别于整个data中的其他成员。这样的标记可以是隐式的(在这种情况下,它们被编码为值的TLV标记,而不是使用基类型作为TLV标记)或显式(其中标记用于包装基类型TLV的构造TLV中)。

编码可以是基本结构类型或是复杂类型的,取决于选择的类型。下边图描述了tag类型的选择,这些定义也是在ASN.1定义中声明的。

tag number值对应类型

tag域编码

识别部分将元素类型编码为ASN.1的tag值,由类型与数字组成,这里也定义了内容部分是基本结构(primitive)或是复杂结构(constructed)。

tag域结构

首个字节中,bit6编码指示了类型是基本类型或是复合类型,bit7, bit8指示了类型,而big1-bit5指示了tag值,也可以有后续字节一同标识tag部分。

bit7,bit8可表示值

bit6可选值

当tag数值太大,超出tag域5位bit(不超过31)可表达的范围时,需要更多字节表示tag。

当首字节中bit1-bit5均是1,tag数值由后续字节表示。当后续字节的bit8位置是1时,表明还有后续字节,bit1-bit7表示tag数值。

tag二进制是大端的(即最高位在左边)

Length域

length域有两个格式的定义:定义格式,未定义格式。

length域首字节

定义格式(Definite)

这部分编码的值表明了内容部分(Content octets)基本结构或复杂结构占的长度。不同长度值的表示范围由一个长格式或一个短格式进行定义。

  • 短格式:一个字节,bit8是0,bit1-bit7编码值表示content部分占用的字节数;
  • 长格式:若干字节,首字节bit8是1,表示后续还有1个或多个字节表示长度值。首字节bit8是1,bit1-bit7(排除0和127)编码值表示内容部分的长度。

长格式示例

未定义格式(Indefinite)

这个格式未编码长度值,但内容部分需要标记字节来结束。

这个格式有一个字节组成,其中bit8是1,bit1-bit7均是0。后续内容部分就是2个字节的EOC符号。

Content域

这个部分的编码值表示了元素的实际数据值。

注意的是值域(value field)也可能没有字节表示,即length域的编码值为0。

Java解析BER-TLV方法封装

Tlv

/**
 * Tlv接口定义,用以自定义TLV实体类进行实现。
 *
 * TLV(type-length-value or tag-length-value) is an encoding theme for optional information
 * element in a certain protocol.
 *
 * The type and length are fixed in size(typically 1-4 bytes) and the value field is of variable
 * size.
 *
 * Type: A binary code, often simply alphanumeric, which indicates the kind of field that this
 * part of the message represents.
 *
 * Length: The size of the value field(typically in bytes).
 *
 * Value: Variable-sized series of bytes which contains data for this part of the message.
 *
 * @author Nicholas Ni
 * @since 2020/8/3 16:37
 */
public class Tlv implements Serializable {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

BerTlv

/**
 * 基本编码规则TLV格式,符合ASN.1标准。
 *
 * 若需要自定义相关的数据处理,可以自定义实体类集成Tlv,且解析类实现TlvParser接口并实现parseTlvList(byte[])方法。
 */
public class BerTlv extends Tlv {

    public int tag;

    public int length;

    public byte[] value;

    public BerTlv() {
    }

    public BerTlv(int tag, int length, byte[] value) {
        this.tag = tag;
        this.length = length;
        this.value = value;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

TlvParser

/**
 * TLVResolvable定义了TLV解析方法,提供默认的解析方式。
 * 由于TLV数据type,length字节数占1-4个字节,可以是变长的,因此在解析前需要设置具体的字节长度——依据约定。
 */
public interface TlvParser extends Resolvable {

    /**
     * 解析TLV列表。
     *
     * @param tlvByteArray TLV字节数组
     */
    default Map<Integer, ? extends Tlv> parseTlvList(byte[] tlvByteArray) {
        System.out.println("This is default implementation.");
        return new TreeMap<>();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

BerTlvParser

/**
 * 解析TLV数据工具类。
 * <p>
 * 使用:
 * <p>
 * <p>
 * 直接使用创建,匿名类方式进行创建,可以重写部分方法,灵活设置type,length长度。
 */
public final class BerTlvParser implements TlvParser {

    /**
     * 依据byte数组解析TLV列表数据。
     *
     * @param data   包含TLV数据的字节数组
     * @param offset TLV数据的开始索引
     * @param length TLV数据长度
     * @return tlv map
     */
    public Map<Integer, BerTlv> parseTLVList(byte[] data, int offset, int length) {
        byte[] tlvByteArray = new byte[length];
        System.arraycopy(data, offset, tlvByteArray, 0, length);

        return parseTlvList(tlvByteArray);
    }

    /**
     * 依据byte数组解析TLV列表数据。
     *
     * @param data TLV字节数组
     * @return TLV map
     */
    @Override
    public final Map<Integer, BerTlv> parseTlvList(byte[] data) {
        Objects.requireNonNull(data, "Byte array indicating TLV list can not be null!");

        if (data.length == 0) {
            return new TreeMap<>();
        }

        int currIndex = -1;
        int tag = data[++currIndex] & 0xFF;
        if ((tag & 0x20) != 0x20) {
            // 解析基本结构类型(单一结构)
            return parsePrimitiveData(data);
        }
        // 解析复合结构类型
        return parseComprehensiveData(data);
    }

    /**
     * 解析复合类型的TLV数据结构 —— 即嵌套的TLV结构。
     *
     * @param data 解析的数据
     * @return 完成解析的TLV map
     */
    private Map<Integer, BerTlv> parseComprehensiveData(byte[] data) {
        final Map<Integer, BerTlv> tlvMap = new TreeMap<>();
        parseComprehensiveData(data, tlvMap, 0);

        System.out.println(
                String.format(Locale.getDefault(),
                              "Parsed result size: %d",
                              tlvMap.size())
        );
        return tlvMap;
    }

    /**
     * 解析复杂类型TLV结构。
     *
     * @param data   解析的数据
     * @param retMap 存储结果的TLV map
     * @param index  当前索引位置
     */
    private void parseComprehensiveData(byte[] data,
                                        Map<Integer, BerTlv> retMap,
                                        int index) {
        int currIndex = index;
        if (currIndex >= data.length) {
            return;
        }

        final int lengthTag = sizeTag(data, currIndex);
        currIndex += lengthTag;
        final int lengthLen = sizeLength(data, currIndex);
        currIndex += lengthLen;

        int currentByte = data[currIndex] & 0xFF;
        // 单一结构
        while ((currentByte & 0x20) != 0x20) {
            currIndex += parsePrimitiveData(data, retMap, currIndex);
            if (currIndex >= data.length) {
                break;
            }
            currentByte = data[currIndex] & 0xFF;
        }

        if (currIndex >= data.length) {
            return;
        }

        parseComprehensiveData(data, retMap, currIndex);

    }

    /**
     * 解析复合结构内单一结构(无法再分)的TLV数据。
     *
     * @param data   解析的数据
     * @param retMap 存储结果的TLV map
     * @param index  当前索引位置
     * @return 当前TLV实体的长度
     */
    private int parsePrimitiveData(byte[] data,
                                   Map<Integer, BerTlv> retMap,
                                   int index) {
        if (index >= data.length) {
            return 0;
        }

        final BerTlv berTlv = new BerTlv();
        int currIndex = index;

        final int lengthOfTag = sizeTag(data, currIndex);
        berTlv.tag = parseTagNumber(data, currIndex);
        currIndex += lengthOfTag;

        final int lengthOfLength = sizeLength(data, currIndex);
        berTlv.length = parseLength(data, currIndex);
        currIndex += lengthOfLength;

        final int lengthOfValue = berTlv.length;
        berTlv.value = parseValueArray(data, currIndex, lengthOfValue);
        currIndex += lengthOfValue;

        retMap.put(berTlv.tag, berTlv);
        return currIndex - index;
    }

    /**
     * 解析单一结构(primitive constructed data) TLV数据。
     *
     * @param data TLV数据
     * @return 解析后的TLV map
     */
    private Map<Integer, BerTlv> parsePrimitiveData(byte[] data) {
        final BerTlv berTlv = new BerTlv();
        int currIndex = 0;

        final int lengthOfTag = sizeTag(data, currIndex);
        berTlv.tag = parseTagNumber(data, currIndex);
        currIndex += lengthOfTag;

        final int lengthOfLength = sizeLength(data, currIndex);
        berTlv.length = parseLength(data, currIndex);
        currIndex += lengthOfLength;

        final int lengthOfValue = berTlv.length;
        berTlv.value = parseValueArray(data, currIndex, lengthOfValue);
        currIndex += lengthOfValue;

        System.out.println(String.format(Locale.getDefault(), "final index: %d", currIndex));

        if (currIndex < data.length) {
            throw new RuntimeException(
                    "Parse primitive constructed data, value length does not match length field value."
            );
        }

        return new TreeMap<Integer, BerTlv>() {
            {
                put(berTlv.tag, berTlv);
            }
        };
    }

    /**
     * 解析获取tlv字节数组中表示value的字节数组,返回原始的字节数组。
     *
     * @param data   TLV字节数组
     * @param offset 当前索引
     * @param length 表示value的字节数组的长度
     * @return 表示value的字节数组
     */
    private byte[] parseValueArray(byte[] data, int offset, int length) {
        byte[] valueArray = new byte[length];
        System.arraycopy(data, offset, valueArray, 0, length);
        return valueArray;
    }

    /**
     * 解析tag域,并返回tag域的值。
     * <p>
     * 如: 4F 05 48656C6C6F
     * 解析过程:
     * <ul>
     *     <li>'4F'(01001111) 是tag域的字段,其高三位中前两位表示解析的信息类型,第三位表示TLV是基本结构类型或是复合类型的结构,暂且不计,用途不影响解析;</li>
     *     <li>低5位(01111),最高位若不是1,则剩余4位即可标识TLV实体的标号;若5位均是1(11111),则表示后续还有tag域的字节;</li>
     * </ul>
     *
     * @param data   TLV字节数组
     * @param offset 当前偏移值(索引)
     * @return tag值
     */
    private int parseTagNumber(byte[] data, int offset) {
        int byteTag = data[offset] & 0xFF;

        if ((byteTag & 0x1F) < 0x1F) {
            return byteTag & 0x1F;
        }

        int indexTag = offset + 1;
        byteTag = data[indexTag] & 0xFF;
        while (((byteTag >>> 7) & 0x01) == 0x01) {
            byteTag = data[++indexTag];
        }
        int tagNumber = 0;
        // 表示tag number的字节数
        int lenTagNumber = indexTag - offset;
        byte[] tagNumberByes = new byte[lenTagNumber];
        System.arraycopy(data, offset + 1, tagNumberByes, 0, lenTagNumber);
        switch (lenTagNumber) {
            case 1: {
                tagNumber = tagNumberByes[0] & 0xFF;
                break;
            }

            case 2: {
                tagNumber = ((tagNumberByes[0] & 0xFF) << 8) | (tagNumberByes[1] & 0xFF);
                break;
            }

            case 3: {
                tagNumber = ((tagNumberByes[0] & 0xFF) << 16) |
                        ((tagNumberByes[1] & 0xFF) << 8) | (tagNumberByes[2] & 0xFF);
                break;
            }

            case 4: {
                tagNumber = ((tagNumberByes[0] & 0xFF) << 24) |
                        ((tagNumberByes[1] & 0xFF) << 16) |
                        ((tagNumberByes[2] & 0xFF) << 8) |
                        (tagNumberByes[2] & 0xFF);
                break;
            }
        }
        if (tagNumber < 0x1F) {
            throw new RuntimeException(
                    "Invalid tag number, code < 31, but len of tag field is " + (lenTagNumber + 1)
                            + ", index = " + offset
            );
        }
        return tagNumber;
    }

    /**
     * 解析获取length域的值,即tag-length-value中length值。
     *
     * @param data   TLV字节数组
     * @param offset 当前偏移值(索引)
     * @return length值
     */
    private int parseLength(byte[] data, int offset) {
        int byteLength = data[offset] & 0xFF;

        if (byteLength < 0x80) {
            return byteLength & 0x7F;
        }

        if (byteLength == 0x80) {
            throw new RuntimeException("Invalid length field code = 0x80!");
        }

        final int numLenBytes = byteLength & 0x7F;
        byte[] lengthBytes = new byte[numLenBytes];
        System.arraycopy(data, offset + 1, lengthBytes, 0, numLenBytes);
        int valLength = 0;
        switch (lengthBytes.length) {
            case 1: {
                valLength = lengthBytes[0] & 0xFF;
                break;
            }

            case 2: {
                valLength = ((lengthBytes[0] & 0xFF) << 8) | (lengthBytes[1] & 0xFF);
                break;
            }

            case 3: {
                valLength = ((lengthBytes[0] & 0xFF) << 16) |
                        ((lengthBytes[1] & 0xFF) << 8) |
                        (lengthBytes[2] & 0xFF);
                break;
            }

            case 4: {
                valLength = ((lengthBytes[0] & 0xFF) << 24) |
                        ((lengthBytes[1] & 0xFF) << 16) |
                        ((lengthBytes[2] & 0xFF) << 8) |
                        (lengthBytes[3] & 0xFF);
                break;
            }
        }
        return valLength;
    }

    /**
     * 解析tag域长度,判断依据:
     * </p>
     * <ul>
     *      <li>判断tag域首字节低5位是否全为1,若全是1 则表示tag域还有后续字节表示tag编号;</li>
     *      <li>若有后续字节,查看最高位,若最高位是1,则表示后续依然有字节表示tag,0则结束。</li>
     * </ul>
     *
     * @param data   tlv字节数组
     * @param offset 当前偏移位(索引位置)
     * @return tag域占的字节数
     */
    private int sizeTag(byte[] data, int offset) {
        int indexTag = offset;
        int byteTag = data[indexTag] & 0xFF;

        if ((byteTag & 0x1F) == 0x1F) {
            byteTag = data[++indexTag] & 0xFF;
            while (((byteTag >>> 7) & 0x01) == 1) {
                byteTag = data[++indexTag] & 0xFF;
            }
        }

        return indexTag - offset + 1;
    }

    /**
     * BER-TLV中的长度表示Value域中的数据长度,由1到多个字节组成。
     * <p>
     * 解析length域占的字节数,判断依据:
     * <ul>
     *     <li>如果首字节的最高位为0,则低7位表示长度,数据长度最大值为127;</li>
     *     <li>如果首字节的最高位为1,则表明Value域中的数据长度超过127,其低7位表示后续LEN域的字节数。</li>
     * </ul>
     *
     * @param data   tlv字节数组
     * @param offset 当前偏移位(索引位置)
     * @return length域占的字节数
     */
    private int sizeLength(byte[] data, int offset) {
        int byteLength = data[offset] & 0xFF;
        int sizeLength = 1;

        if (((byteLength >>> 7) & 0x01) == 0x01) {
            sizeLength += (byteLength & 0x7F);
        }

        return sizeLength;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/345843
推荐阅读
相关标签
  

闽ICP备14008679号