当前位置:   article > 正文

PBOC2.0/EMV之TLV格式解析(C++)_tlv长度规范

tlv长度规范

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分成两个数据元结构,一种是简单数据元结构,一种是复合数据元架构:

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

 

   复合数据元结构:

 

  后面的Value说明:Primitive or constructed BER-TLV data object number,包含一个简单数据元结构或者也可以是一个符合数据元结构。这样可以看出,算法中必须要实现Tag的嵌套功能,递归算法不可少。

5.TLV格式数据实例

数据: 5F2D027A68

Tag域

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

Length域

02 → 00000010 → 02(2字节)

Value域

7A68

6.算法实现( C++)

       根据以上的说明现在来实现它的打包解包的算法(打包的目的是将一个从终端上发的请求数据——字节数组,构造成一系列的TLV结构实体;解包的目的刚好相反,就是将TLV结构实体解析成字节数组,然后通过IC卡发送到终端上)。

      首先定义一个TLV结构实体:

  1. // TLV结构体
  2. struct TLVEntity {
  3. unsigned char* Tag; //标记
  4. unsigned char* Length; //数据长度
  5. unsigned char* Value; //数据
  6. unsigned int TagSize; //标记占用字节数
  7. unsigned int LengthSize; //数据长度占用字节数
  8. TLVEntity* Sub_TLVEntity; //子嵌套TLV实体
  9. };
    其中TagSize代表Tag字段的字节长度,LengthSize代表Length的字节长度,这里的Length记住要使用char*,由于前面说过,Length可能包含
多个字节,通过多个字节确定Value域的长度,Sub_TLVEntity作为子嵌套的TLV结构体。

    定义一个TLVPackage的打包类:

TLVPackage.h:

  1. // TLV打包类
  2. class TLVPackage
  3. {
  4. public:
  5. TLVPackage();
  6. virtual ~TLVPackage();
  7. //构造TLV实体
  8. static void Construct(unsigned char* buffer, unsigned int bufferLength, TLVEntity* tlvEntity, unsigned int& entityLength, unsigned int status=0);
  9. //解析TLV字节数组
  10. static void Parse(TLVEntity* tlvEntity, unsigned int entityLength, unsigned char* buffer, unsigned int& bufferLength);
  11. };
具体实现 方法

  1. // 构造TLV
  2. void TLVPackage:: Construct(
  3. unsigned char* buffer,
  4. unsigned int bufferLength,
  5. TLVEntity* tlvEntity,
  6. unsigned int& entityLength,
  7. unsigned int status
  8. )
  9. {
  10. int currentTLVIndex = 0;
  11. int currentIndex = 0;
  12. int currentStatus = 'T'; //状态字符
  13. unsigned long valueSize = 0;
  14. while(currentIndex < bufferLength)
  15. {
  16. switch(currentStatus)
  17. {
  18. case 'T':
  19. valueSize = 0;
  20. //判断是否单一结构
  21. if((status == 1 && buffer[currentIndex] & 0x20) != 0x20)
  22. {
  23. tlvEntity[currentTLVIndex].Sub_TLVEntity = NULL; //单一结构时将子Tag置空
  24. //判断是否多字节Tag
  25. if((buffer[currentIndex] & 0x1f) == 0x1f)
  26. {
  27. int endTagIndex = currentIndex;
  28. while((buffer[++endTagIndex] & 0x80) == 0x80); //判断第二个字节的最高位是否为1
  29. int tagSize = endTagIndex - currentIndex + 1; //计算Tag包含多少字节
  30. tlvEntity[currentTLVIndex].Tag = new unsigned char[tagSize];
  31. memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, tagSize);
  32. tlvEntity[currentTLVIndex].Tag[tagSize] = 0;
  33. tlvEntity[currentTLVIndex].TagSize = tagSize;
  34. currentIndex += tagSize;
  35. }
  36. else
  37. {
  38. tlvEntity[currentTLVIndex].Tag = new unsigned char[1];
  39. memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, 1);
  40. tlvEntity[currentTLVIndex].Tag[1] = 0;
  41. tlvEntity[currentTLVIndex].TagSize = 1;
  42. currentIndex += 1;
  43. }
  44. }
  45. else
  46. {
  47. //判断是否多字节Tag
  48. if((buffer[currentIndex] & 0x1f) == 0x1f)
  49. {
  50. int endTagIndex = currentIndex;
  51. while((buffer[++endTagIndex] & 0x80) == 0x80); //判断第二个字节的最高位是否为1
  52. int tagSize = endTagIndex - currentIndex + 1; //计算Tag包含多少字节
  53. tlvEntity[currentTLVIndex].Tag = new unsigned char[tagSize];
  54. memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, tagSize);
  55. tlvEntity[currentTLVIndex].Tag[tagSize] = 0;
  56. tlvEntity[currentTLVIndex].TagSize = tagSize;
  57. currentIndex += tagSize;
  58. }
  59. else
  60. {
  61. tlvEntity[currentTLVIndex].Tag = new unsigned char[1];
  62. memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, 1);
  63. tlvEntity[currentTLVIndex].Tag[1] = 0;
  64. tlvEntity[currentTLVIndex].TagSize = 1;
  65. currentIndex += 1;
  66. }
  67. //分析SubTag
  68. int subLength = 0;
  69. unsigned char* temp;
  70. if((buffer[currentIndex] & 0x80) == 0x80)
  71. {
  72. for (int index = 0; index < 2; index++)
  73. {
  74. subLength += buffer[currentIndex + 1 + index] << (index * 8); //计算Length域的长度
  75. }
  76. temp = new unsigned char[subLength];
  77. memcpy(temp, buffer + currentIndex + 3, subLength);
  78. }
  79. else
  80. {
  81. subLength = buffer[currentIndex];
  82. temp = new unsigned char[subLength];
  83. memcpy(temp, buffer + currentIndex + 1, subLength);
  84. }
  85. temp[subLength] = 0;
  86. //memcpy(temp, buffer + currentIndex + 1, subLength);
  87. unsigned int oLength;
  88. tlvEntity[currentTLVIndex].Sub_TLVEntity = new TLVEntity[1];
  89. Construct(temp, subLength, tlvEntity[currentTLVIndex].Sub_TLVEntity, oLength);
  90. }
  91. currentStatus = 'L';
  92. break;
  93. case 'L':
  94. //判断长度字节的最高位是否为1,如果为1,则该字节为长度扩展字节,由下一个字节开始决定长度
  95. if((buffer[currentIndex] & 0x80) != 0x80)
  96. {
  97. tlvEntity[currentTLVIndex].Length = new unsigned char[1];
  98. memcpy(tlvEntity[currentTLVIndex].Length, buffer + currentIndex, 1);
  99. tlvEntity[currentTLVIndex].Length[1] = 0;
  100. tlvEntity[currentTLVIndex].LengthSize = 1;
  101. valueSize = tlvEntity[currentTLVIndex].Length[0];
  102. currentIndex += 1;
  103. }
  104. else
  105. {
  106. //为1的情况
  107. unsigned int lengthSize = buffer[currentIndex] & 0x7f;
  108. currentIndex += 1; //从下一个字节开始算Length域
  109. for (int index = 0; index < lengthSize; index++)
  110. {
  111. valueSize += buffer[currentIndex + index] << (index * 8); //计算Length域的长度
  112. }
  113. tlvEntity[currentTLVIndex].Length = new unsigned char[lengthSize];
  114. memcpy(tlvEntity[currentTLVIndex].Length, buffer + currentIndex, lengthSize);
  115. tlvEntity[currentTLVIndex].Length[lengthSize] = 0;
  116. tlvEntity[currentTLVIndex].LengthSize = lengthSize;
  117. currentIndex += lengthSize;
  118. }
  119. currentStatus = 'V';
  120. break;
  121. case 'V':
  122. tlvEntity[currentTLVIndex].Value = new unsigned char[valueSize];
  123. memcpy(tlvEntity[currentTLVIndex].Value, buffer + currentIndex, valueSize);
  124. tlvEntity[currentTLVIndex].Value[valueSize] = 0;
  125. currentIndex += valueSize;
  126. //进入下一个TLV构造循环
  127. currentTLVIndex += 1;
  128. currentStatus = 'T';
  129. break;
  130. default:
  131. return;
  132. }
  133. }
  134. entityLength = currentTLVIndex;
  135. }
  136. // 解析TLV
  137. void TLVPackage::Parse(
  138. TLVEntity* tlvEntity,
  139. unsigned int entityLength,
  140. unsigned char* buffer,
  141. unsigned int& bufferLength
  142. )
  143. {
  144. int currentIndex = 0;
  145. int currentTLVIndex = 0;
  146. unsigned long valueSize = 0;
  147. while(currentTLVIndex < entityLength)
  148. {
  149. valueSize = 0;
  150. TLVEntity entity = tlvEntity[currentTLVIndex];
  151. memcpy(buffer + currentIndex, entity.Tag, entity.TagSize); //解析Tag
  152. currentIndex += entity.TagSize;
  153. for (int index = 0; index < entity.LengthSize; index++)
  154. {
  155. valueSize += entity.Length[index] << (index * 8); //计算Length域的长度
  156. }
  157. if(valueSize > 127)
  158. {
  159. buffer[currentIndex] = 0x80 | entity.LengthSize;
  160. currentIndex += 1;
  161. }
  162. memcpy(buffer + currentIndex, entity.Length, entity.LengthSize); //解析Length
  163. currentIndex += entity.LengthSize;
  164. //判断是否包含子嵌套TLV
  165. if(entity.Sub_TLVEntity == NULL)
  166. {
  167. memcpy(buffer + currentIndex, entity.Value, valueSize); //解析Value
  168. currentIndex += valueSize;
  169. }
  170. else
  171. {
  172. unsigned int oLength;
  173. Parse(entity.Sub_TLVEntity, 1, buffer + currentIndex, oLength); //解析子嵌套TLV
  174. currentIndex += oLength;
  175. }
  176. currentTLVIndex++;
  177. }
  178. buffer[currentIndex] = 0;
  179. bufferLength = currentIndex;
  180. }
然后写测试程序:

  1. // 上发测试数据
  2. unsigned char requestBuf[] = {
  3. 0x9F, 0x1C, 0x12, 0x33, 0x33, 0x30, 0x32, 0x32, 0x37, 0x31, 0x39, 0x36, 0x32, 0x30, 0x34, 0x30, 0x34,
  4. 0x32, 0x37, 0x31, 0x38, 0x9F, 0x62, 0x01, 0x01, 0x57, 0x12, 0x62, 0x22, 0x89, 0x00, 0x00, 0x02, 0x91,
  5. 0x01, 0xD0, 0x90, 0x32, 0x01, 0x02, 0x47, 0x17, 0x13, 0x00, 0x0F, 0x5F, 0x20, 0x0A, 0x48, 0x55, 0x47,
  6. 0x55, 0x4F, 0x20, 0x4D, 0x49, 0x4E, 0x47, 0x9F, 0x1F, 0x3C, 0x25, 0x39, 0x39, 0x36, 0x32, 0x32, 0x32,
  7. 0x38, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x39, 0x31, 0x30, 0x31, 0x5E, 0x47, 0x55, 0x4F, 0x20,
  8. 0x4D, 0x49, 0x4E, 0x47, 0x2F, 0x48, 0x55, 0x5E, 0x30, 0x39, 0x30, 0x33, 0x32, 0x30, 0x31, 0x30, 0x32,
  9. 0x34, 0x37, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x38, 0x39, 0x30,
  10. 0x30, 0x3F
  11. };
  12. TLVEntity tlvEntity[TLV_MAX_LENGTH];
  13. unsigned int tlv_count;
  14. //构造TLV
  15. TLVPackage::Construct(requestBuf, sizeof(requestBuf), tlvEntity, tlv_count);
  16. unsigned char parseBuf[1024];
  17. unsigned int buf_count;
  18. //解析TLV
  19. TLVPackage::Parse(tlvEntity, tlv_count, parseBuf, buf_count);
  20. if(strncmp((char*)parseBuf, (char*)requestBuf, sizeof(requestBuf)) == 0)
  21. {
  22. AfxMessageBox("TRUE");
  23. }
  24. else
  25. {
  26. AfxMessageBox("FALSE");
  27. }
   最后测试结果中,可以得到最后将弹出“TRUE”的对话框。证明构造TLV得到的TLVEntity,再对这个实体进行解析,可以的到解析后的字节
数组, 最后通过strncmp的方法比较判断,是否原始字节数组和解析后的字节数组是否一致。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/497275
推荐阅读
相关标签
  

闽ICP备14008679号