EMV/PBOC解析(三) TLV格式解析(C#)

1.什么是TLV格式?

TLV即Tag-Length-Value,常在IC卡与POS终端设备中通过这样的一个应用通信协议进行数据交换。 金融系统中的TLV是BER-TLV编码的一个特例编码规范,而BER-TLV是ISO定义中的规范。在TLV的定义中,可以知道它包括三个域,分别为:标签域(Tag),长度域(Length),内容域(Value)。这里的长度域的值实际上就是内容域的长度。 其实,在BER编码的方式有两种情况,一种是确定长度的方式,一种是不确定长度的方式,而金融TLV选择了确定长度的方式,这样在设备之间的数据传输量上就可以减少。

2.Tag域

3.Length域

当b8为0时,该字节的b7-b1作为value域的长度;当b8为1时,b7-b1作为后续字节的长度。例:10000011,代表后续还有3个字节作为value域的长度(本字节不算,本字节变为作为一个Length的索引)。3个字节代表value的长度,意味着什么呢,意味着内容的长度当需要很大的时候,字节的位数就会跟着越高,3个字节就代表最大可以有256*256*256的长度。

4.Value域

分成两种情况考虑,就是前面说到的Tag分成两个数据元结构,一种是简单数据元结构,一种是复合数据元架构:

先来看看简单数据元结构:

 

复合数据元结构:

 

5.TLV格式数据实例

数据: 5F2D027A68

Tag域

5F → 01011111 → 5F 2D → 00101101 → 5F2D

Length域

02 → 00000010 → 02(2字节)

Value域

7A68

6.算法实现 C#

首先根据定义创建一个实体类

 1 /// <summary>
 2     /// TLV格式报文实体类
 3     /// </summary>
 4     public class TLVEntity
 5     {
 6         /// <summary>
 7         /// 标记
 8         /// </summary>
 9         public byte[] Tag { get; set; }
10 
11         /// <summary>
12         /// 数据长度
13         /// </summary>
14         public byte[] Length { get; set; }
15 
16         /// <summary>
17         /// 数据
18         /// </summary>
19         public byte[] Value { get; set; }
20 
21         /// <summary>
22         /// 标记占用字节数
23         /// </summary>
24         public int TagSize { get { return this.Tag.Length; } }
25 
26         /// <summary>
27         /// 数据长度占用字节数
28         /// </summary>
29         public int LengthSize { get { return this.Length.Length; } }
30 
31         /// <summary>
32         /// 子嵌套TLV实体列表
33         /// </summary>
34         public List<TLVEntity> SubTLVEntity { get; set; }
35     }

下面是tlv格式报文打包解析

  1    /// <summary>
  2     /// TLV格式报文打包解析
  3     /// </summary>
  4     public class TLVPackage
  5     {
  6         #region TLV 打包
  7         
  8         /// <summary>
  9         /// TLV报文打包
 10         /// </summary>
 11         /// <param name="buffer">字节数据</param>
 12         /// <returns></returns>
 13         public static List<TLVEntity> Construct(byte[] buffer)
 14         {
 15             List<TLVEntity> resultList = new List<TLVEntity>();
 16             int currentIndex = 0;
 17             while (currentIndex < buffer.Length)
 18             {
 19                 TLVEntity entity = new TLVEntity();
 20                 //1. 根据Tag判断数据是否是嵌套的TLV
 21                 bool hasSubEntity = HasSubEntity(buffer, currentIndex);
 22 
 23                 #region Tag解析
 24                 entity.Tag = GetTag(buffer, currentIndex);
 25                 currentIndex += entity.Tag.Length;
 26                 #endregion
 27 
 28                 #region Length解析
 29                 entity.Length = GetLength(buffer, currentIndex);
 30                 currentIndex += entity.Length.Length;
 31                 #endregion
 32 
 33                 #region Value解析
 34                 int valueLength = GetValueLengthByLengthByteValue(entity.Length);
 35                 entity.Value = buffer.Take(currentIndex + valueLength).Skip(currentIndex).ToArray();
 36                 if (hasSubEntity)//判断是否是嵌套结构
 37                     entity.SubTLVEntity = Construct(entity.Value);//嵌套结构递归解析
 38                 currentIndex += entity.Value.Length;
 39                 #endregion
 40 
 41                 resultList.Add(entity);
 42             }
 43             return resultList;
 44         }
 45 
 46         /// <summary>
 47         /// 是否存在嵌套实体
 48         /// </summary>
 49         /// <returns></returns>
 50         private static bool HasSubEntity(byte[] bytes, int index)
 51         {
 52             if (bytes.Length < index + 1)
 53                 throw new ArgumentException("无效的索引值");
 54             return (bytes[index] & 0x20) == 0x20;
 55         }
 56 
 57         /// <summary>
 58         /// 获取Tag字节数据
 59         /// </summary>
 60         /// <param name="bytes">长度</param>
 61         /// <param name="index">索引位置</param>
 62         /// <returns></returns>
 63         private static byte[] GetTag(byte[] bytes, int index)
 64         {
 65             if (bytes.Length < index + 1)
 66                 throw new ArgumentException("无效的索引值");
 67             //判断Tag所占字节长度
 68             if ((bytes[index] & 0x1f) == 0x1f)
 69             {//占2字节
 70                 return new byte[] { bytes[index], bytes[index + 1] };
 71             }
 72             else
 73             {//占1字节
 74                 return new byte[] { bytes[index] };
 75             }
 76         }
 77 
 78         /// <summary>
 79         /// 获取长度
 80         /// </summary>
 81         /// <param name="bytes">长度</param>
 82         /// <param name="index">索引位置</param>
 83         /// <returns></returns>
 84         private static byte[] GetLength(byte[] bytes, int index)
 85         {
 86             if (bytes.Length < index + 1)
 87                 throw new ArgumentException("无效的索引值");
 88             //判断Length部分所占字节 是1个字节还是多个字节
 89             if ((bytes[index] & 0x80) == 0x80)
 90             {//占多个字节
 91                 int lengthSize = (bytes[index] & 0x7f) + 1;//获取Length所占字节数
 92                 return bytes.Take(index + lengthSize).Skip(index).ToArray();
 93             }
 94             else
 95             {//占单个字节
 96                 return new byte[] { bytes[index] };
 97             }
 98         }
 99         /// <summary>
100         /// 根据Length部分的值获取到value部分的值
101         /// </summary>
102         /// <param name="bytes">Length部分的值</param>
103         /// <returns></returns>
104         private static int GetValueLengthByLengthByteValue(byte[] bytes)
105         {
106             int length = 0;
107             if (bytes.Length == 1)
108                 length = bytes[0];
109             else
110             {
111                 //从下一个字节开始算Length域
112                 for (int index = 1; index < bytes.Length; index++)
113                 {
114                     length += bytes[index] << ((index-1) * 8); //计算Length域的长度
115                 }
116             }
117             return length;
118         }
119  
120     #endregion
121 
122         #region TLV 解析
123         /// <summary>
124         /// 解析TLV
125         /// </summary>
126         /// <param name="list">
127         /// <returns></returns>
128         public static byte[] Parse(List<TLVEntity> list)
129         {
130             byte[] buffer = new byte[4096];
131             int currentIndex = 0;
132             int currentTLVIndex = 0;
133             int valueSize = 0;
134 
135             while (currentTLVIndex < list.Count())
136             {
137                 valueSize = 0;
138                 TLVEntity entity = list[currentTLVIndex];
139 
140                 Array.Copy(entity.Tag, 0, buffer, currentIndex, entity.TagSize);    //解析Tag
141 
142                 currentIndex += entity.TagSize;
143 
144                 for (int index = 0; index < entity.LengthSize; index++)
145                 {
146                     valueSize += entity.Length[index] << (index * 8); //计算Length域的长度
147                 }
148                 if (valueSize > 127)
149                 {
150                     buffer[currentIndex] = Convert.ToByte(0x80 | entity.LengthSize);
151                     currentIndex += 1;
152                 }
153 
154                 Array.Copy(entity.Length, 0, buffer, currentIndex, entity.LengthSize);  //解析Length
155 
156                 currentIndex += entity.LengthSize;
157                 //判断是否包含子嵌套TLV
158                 if (entity.SubTLVEntity == null)
159                 {
160                     Array.Copy(entity.Value, 0, buffer, currentIndex, valueSize);   //解析Value
161                     currentIndex += valueSize;
162                 }
163                 else
164                 {
165                     byte[] tempBuffer = Parse(entity.SubTLVEntity);
166                     Array.Copy(tempBuffer, 0, buffer, currentIndex, tempBuffer.Length); //解析子嵌套TLV
167                     currentIndex += tempBuffer.Length;
168                 }
169 
170                 currentTLVIndex++;
171             }
172 
173             byte[] resultBuffer = new byte[currentIndex];
174             Array.Copy(buffer, 0, resultBuffer, 0, currentIndex);
175 
176             return resultBuffer;
177         } 
178         #endregion
179     }

tlv实体操作帮助类

   public class TLVHelper
    {

        /// <summary>
        /// 根据tag获取tlv的值
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="tag"></param>
        /// <returns></returns>
        public static TLVEntity GetValueByTag(List<TLVEntity> entities, string tag)
        {
            TLVEntity resultEntity = null;
            var query = entities.SingleOrDefault(e => CodeConvert.ToHexString(e.Tag).ToUpper() == tag);
            if (query == null)
            {
                foreach (var tlv in entities)
                {
                    if (tlv.SubTLVEntity != null)
                    {
                        TLVEntity result = GetValueByTag(tlv.SubTLVEntity, tag);

                        if (result !=null && result.Length.Length > 0)
                            return result;
                    }
                }
            }
            else
                resultEntity = query;
            return resultEntity;
        }
        /// <summary>
        /// 16进制数据转化为TVL实体
        /// </summary>
        /// <param name="resultData"></param>
        /// <returns></returns>
        public static List<TLVEntity> ToTLVEntityList(string data)
        {
            byte[] dataBytes = CodeConvert.HexStringToByteArray(data);
            var tlvList = TLVPackage.Construct(dataBytes);
            return tlvList;
        }
        
    }

转载请注明出处:http://www.cnblogs.com/xinwang/p/5733198.html

 

posted @ 2016-08-03 15:46  CharlesShang  阅读(8921)  评论(3编辑  收藏  举报