赞
踩
BER-TLV中BER是Basic Encoding Rules的简写,是ASN.1的一种编码格式,它是将抽象信息编码成为具体数据流的最初规则。
相关知识及定义来自:X.690
BER格式描述了自我描述与分界的数据结构。每个数据编码为一个由type标识,length描述,data数据的元素,并且在必要的位置添加结束标识。这个格式可以在不需要提前了解数据的大小,内容,及数据意义情况下允许接收方从数据流(data inputstream)中解码数据。
这是4个部分组成的一个单元组,末尾的八元组(字节)是可选的,只在长度形式未定义情况下使用。内容(Contents octets)部分也可能在为null的情况下被移除。
这部分数据(尤其是序列(sequences),集合(sets),选项(choices)的成员)由唯一tag数标识,区别于整个data中的其他成员。这样的标记可以是隐式的(在这种情况下,它们被编码为值的TLV标记,而不是使用基类型作为TLV标记)或显式(其中标记用于包装基类型TLV的构造TLV中)。
编码可以是基本结构类型或是复杂类型的,取决于选择的类型。下边图描述了tag类型的选择,这些定义也是在ASN.1定义中声明的。
识别部分将元素类型编码为ASN.1的tag值,由类型与数字组成,这里也定义了内容部分是基本结构(primitive)或是复杂结构(constructed)。
首个字节中,bit6编码指示了类型是基本类型或是复合类型,bit7, bit8指示了类型,而big1-bit5指示了tag值,也可以有后续字节一同标识tag部分。
当tag数值太大,超出tag域5位bit(不超过31)可表达的范围时,需要更多字节表示tag。
当首字节中bit1-bit5均是1,tag数值由后续字节表示。当后续字节的bit8位置是1时,表明还有后续字节,bit1-bit7表示tag数值。
tag二进制是大端的(即最高位在左边)
length域有两个格式的定义:定义格式,未定义格式。
这部分编码的值表明了内容部分(Content octets)基本结构或复杂结构占的长度。不同长度值的表示范围由一个长格式或一个短格式进行定义。
这个格式未编码长度值,但内容部分需要标记字节来结束。
这个格式有一个字节组成,其中bit8是1,bit1-bit7均是0。后续内容部分就是2个字节的EOC符号。
这个部分的编码值表示了元素的实际数据值。
注意的是值域(value field)也可能没有字节表示,即length域的编码值为0。
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 { }
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; } }
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<>(); } }
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; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。