金融系统中PBOC/EMV的TLV的算法实现(含C++/C#)

金融系统中PBOC/EMV的TLV的算法实现(含C++/C#)

 

 

TLV即Tag-Length-Value,常在IC卡与POS终端设备中通过这样的一个应用通信协议进行数据交换。在金融系统以及认证中,PBOC以及 EMV的认证规范文档上面也有对TLV做了一些说明,由于认证规范都是英文文档,所以有些人可能不易于理解。首先我先介绍下什么是TLV,TLV的用途是 什么,以及如何实现它的打包解包算法。

 

      金融系统中的TLV是BER-TLV编码的一个特例编码规范,而BER-TLV是ISO定义中的规范。在TLV的定义中,可以知道它包括三个域,分别为:标签域(Tag),长度域(Length),内容域(Value)。这里的长度域的值实际上就是内容域的长度

      其实,在BER编码的方式有两种情况,一种是确定长度的方式,一种是不确定长度的方式,而金融TLV选择了确定长度的方式,这样在设备之间的数据传输量上就可以减少。

 

Tag域说明:

      先来看下TLV的Tag是如何编码的,先看下图:

image

 

这张图说明了Tag域第一个字节的编码格式。其中b8-b1代表1个字节中的8个位。

其中b8,b7代表数据的类别。根据2个位的组合,有四种类别:通用类别,应用类别,上下文语境类别,专用类别。这个主要用于在于终端设备交互的时候,确定数据处理的类型。

b6代表的是数据元结构,也就是说它是属于简单数据元结构,还是属于结构(复合)数据元结构。当b6为1的时候,就需要后续的字节进行扩展。也就是说复合的TLV中,value域里也包含一个或多个TLV,这个稍后接着介绍。

当b5-b1代表串行号,当5个位都为1时,需要将tag域扩展到下一个字节中,也就是Tag占2个字节;而当5个位都不全为1时,该Tag域就只占1个字节。

      现在,看下b5-b1:11111的情况:

image

      从图中我们看到BER-TLV编码中,当b8为1时,Tag还需要有后续字节,直到b8为0为止。从EMV文档中的说明,Tag最多只占用2个字节,所以这样就相对比较简单一些了。当b8为0时,该Tag域结束,总共就占用2个字节。

 

Length域说明:

      在文档中没有图片叙述,我自绘一个:

image

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

 

Value域说明:

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

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

     image

Tag就是Tag,没有子标签Tag,基本结构就是T-L-V。

      再看下符合数据元结构:

image

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

 

算法实现:

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

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

1

2

3

4

5

6

7

8

9

// TLV结构体

struct TLVEntity {

    unsigned char* Tag;         //标记

    unsigned char* Length;      //数据长度

    unsigned char* Value;       //数据

    unsigned int TagSize;       //标记占用字节数

    unsigned int LengthSize;    //数据长度占用字节数

    TLVEntity* Sub_TLVEntity;   //子嵌套TLV实体

};

其 中TagSize代表Tag字段的字节长度,LengthSize代表Length的字节长度,这里的Length记住要使用char*,由于前面说 过,Length可能包含多个字节,通过多个字节确定Value域的长度,Sub_TLVEntity作为子嵌套的TLV结构体。

      定义一个TLVPackage的打包类:

TLVPackage.h:

1

2

3

4

5

6

7

8

9

10

11

// TLV打包类

class TLVPackage

{

public:

    TLVPackage();

    virtual ~TLVPackage();

    //构造TLV实体

    static void Construct(unsigned char* buffer, unsigned int bufferLength, TLVEntity* tlvEntity, unsigned int& entityLength, unsigned int status=0);

    //解析TLV字节数组

    static void Parse(TLVEntity* tlvEntity, unsigned int entityLength, unsigned char* buffer, unsigned int& bufferLength);

};

具体方法实现:

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

// 构造TLV

void TLVPackage:: Construct(

    unsigned char* buffer,

    unsigned int bufferLength,

    TLVEntity* tlvEntity,

    unsigned int& entityLength,

    unsigned int status

    )

{

    int currentTLVIndex = 0;

    int currentIndex = 0;

    int currentStatus = 'T'//状态字符

    unsigned long valueSize = 0;

 

    while(currentIndex < bufferLength)

    {

        switch(currentStatus)

        {

        case 'T':

            valueSize = 0;

            //判断是否单一结构

            if((status == 1 && buffer[currentIndex] & 0x20) != 0x20)

            {

                tlvEntity[currentTLVIndex].Sub_TLVEntity = NULL; //单一结构时将子Tag置空

                //判断是否多字节Tag

                if((buffer[currentIndex] & 0x1f) == 0x1f)

                {

                    int endTagIndex = currentIndex;

                    while((buffer[++endTagIndex] & 0x80) == 0x80); //判断第二个字节的最高位是否为1

                    int tagSize = endTagIndex - currentIndex + 1; //计算Tag包含多少字节

 

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[tagSize];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, tagSize);

                    tlvEntity[currentTLVIndex].Tag[tagSize] = 0;

 

                    tlvEntity[currentTLVIndex].TagSize = tagSize;

 

                    currentIndex += tagSize;

                }

                else

                {

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[1];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, 1);

                    tlvEntity[currentTLVIndex].Tag[1] = 0;

 

                    tlvEntity[currentTLVIndex].TagSize = 1;

 

                    currentIndex += 1;

                }

            }

            else

            {

                //判断是否多字节Tag

                if((buffer[currentIndex] & 0x1f) == 0x1f)

                {

                    int endTagIndex = currentIndex;

                    while((buffer[++endTagIndex] & 0x80) == 0x80); //判断第二个字节的最高位是否为1

                    int tagSize = endTagIndex - currentIndex + 1; //计算Tag包含多少字节

 

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[tagSize];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, tagSize);

                    tlvEntity[currentTLVIndex].Tag[tagSize] = 0;

 

                    tlvEntity[currentTLVIndex].TagSize = tagSize;

 

                    currentIndex += tagSize;

                }

                else

                {

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[1];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, 1);

                    tlvEntity[currentTLVIndex].Tag[1] = 0;

 

                    tlvEntity[currentTLVIndex].TagSize = 1;

 

                    currentIndex += 1;             

                }

 

                //分析SubTag

                int subLength = 0; 

                 

                unsigned char* temp;

                if((buffer[currentIndex] & 0x80) == 0x80)

                {

                    for (int index = 0; index < 2; index++)

                    {

                        subLength += buffer[currentIndex + 1 + index] << (index * 8); //计算Length域的长度

                    }

 

                    temp = new unsigned char[subLength];

 

                    memcpy(temp, buffer + currentIndex + 3, subLength);

                }

                else

                {

                    subLength = buffer[currentIndex];

 

                    temp = new unsigned char[subLength];

 

                    memcpy(temp, buffer + currentIndex + 1, subLength);

                }

                temp[subLength] = 0;

                 

                //memcpy(temp, buffer + currentIndex + 1, subLength);

                unsigned int oLength;

                tlvEntity[currentTLVIndex].Sub_TLVEntity = new TLVEntity[1];

                Construct(temp, subLength, tlvEntity[currentTLVIndex].Sub_TLVEntity, oLength);

            }

 

            currentStatus = 'L';

            break;

        case 'L':      

            //判断长度字节的最高位是否为1,如果为1,则该字节为长度扩展字节,由下一个字节开始决定长度

            if((buffer[currentIndex] & 0x80) != 0x80)

            {

                tlvEntity[currentTLVIndex].Length = new unsigned char[1];

                memcpy(tlvEntity[currentTLVIndex].Length, buffer + currentIndex, 1);

                tlvEntity[currentTLVIndex].Length[1] = 0;

                tlvEntity[currentTLVIndex].LengthSize = 1;

 

                valueSize = tlvEntity[currentTLVIndex].Length[0];

 

                currentIndex += 1;

            }

            else

            {

                //为1的情况

 

                unsigned int lengthSize = buffer[currentIndex] & 0x7f;

                 

                currentIndex += 1; //从下一个字节开始算Length域

 

                for (int index = 0; index < lengthSize; index++)

                {

                    valueSize += buffer[currentIndex + index] << (index * 8); //计算Length域的长度

                }

 

                tlvEntity[currentTLVIndex].Length = new unsigned char[lengthSize];

                memcpy(tlvEntity[currentTLVIndex].Length, buffer + currentIndex, lengthSize);

                tlvEntity[currentTLVIndex].Length[lengthSize] = 0;

 

                tlvEntity[currentTLVIndex].LengthSize = lengthSize;

 

                currentIndex += lengthSize;

            }

 

            currentStatus = 'V';

            break;

        case 'V':

            tlvEntity[currentTLVIndex].Value = new unsigned char[valueSize];

            memcpy(tlvEntity[currentTLVIndex].Value, buffer + currentIndex, valueSize);

            tlvEntity[currentTLVIndex].Value[valueSize] = 0;

 

            currentIndex += valueSize;

             

            //进入下一个TLV构造循环

            currentTLVIndex += 1;

 

            currentStatus = 'T';

            break;

        default:

            return;

        }

    }

 

    entityLength = currentTLVIndex;

}

 

// 解析TLV

void TLVPackage::Parse(

    TLVEntity* tlvEntity,

    unsigned int entityLength,

    unsigned char* buffer,

    unsigned int& bufferLength

    )

{

    int currentIndex = 0;

    int currentTLVIndex = 0;

    unsigned long valueSize = 0;

 

    while(currentTLVIndex < entityLength)

    {

        valueSize = 0;

        TLVEntity entity = tlvEntity[currentTLVIndex];

         

        memcpy(buffer + currentIndex, entity.Tag, entity.TagSize);  //解析Tag

        currentIndex += entity.TagSize;

 

        for (int index = 0; index < entity.LengthSize; index++)

        {

            valueSize += entity.Length[index] << (index * 8); //计算Length域的长度

        }

        if(valueSize > 127)

        {

            buffer[currentIndex] = 0x80 | entity.LengthSize;

            currentIndex += 1;

        }

         

        memcpy(buffer + currentIndex, entity.Length, entity.LengthSize);    //解析Length

        currentIndex += entity.LengthSize;

        //判断是否包含子嵌套TLV

        if(entity.Sub_TLVEntity == NULL)

        {

            memcpy(buffer + currentIndex, entity.Value, valueSize); //解析Value

            currentIndex += valueSize;

        }

        else

        {

            unsigned int oLength;

            Parse(entity.Sub_TLVEntity, 1, buffer + currentIndex, oLength); //解析子嵌套TLV

            currentIndex += oLength;

        }

 

        currentTLVIndex++;

    }

    buffer[currentIndex] = 0;

    bufferLength = currentIndex;

}

然后写测试程序:

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

// 上发测试数据

unsigned char requestBuf[] = {

        0x9F, 0x1C, 0x12, 0x33, 0x33, 0x30, 0x32, 0x32, 0x37, 0x31, 0x39, 0x36, 0x32, 0x30, 0x34, 0x30, 0x34,

        0x32, 0x37, 0x31, 0x38, 0x9F, 0x62, 0x01, 0x01, 0x57, 0x12, 0x62, 0x22, 0x89, 0x00, 0x00, 0x02, 0x91,

        0x01, 0xD0, 0x90, 0x32, 0x01, 0x02, 0x47, 0x17, 0x13, 0x00, 0x0F, 0x5F, 0x20, 0x0A, 0x48, 0x55, 0x47,

        0x55, 0x4F, 0x20, 0x4D, 0x49, 0x4E, 0x47, 0x9F, 0x1F, 0x3C, 0x25, 0x39, 0x39, 0x36, 0x32, 0x32, 0x32,

        0x38, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x39, 0x31, 0x30, 0x31, 0x5E, 0x47, 0x55, 0x4F, 0x20,

        0x4D, 0x49, 0x4E, 0x47, 0x2F, 0x48, 0x55, 0x5E, 0x30, 0x39, 0x30, 0x33, 0x32, 0x30, 0x31, 0x30, 0x32,

        0x34, 0x37, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x38, 0x39, 0x30,

        0x30, 0x3F

};

 

    TLVEntity tlvEntity[TLV_MAX_LENGTH];

    unsigned int tlv_count;

    //构造TLV

    TLVPackage::Construct(requestBuf, sizeof(requestBuf), tlvEntity, tlv_count);

 

    unsigned char parseBuf[1024];

    unsigned int buf_count;

    //解析TLV

    TLVPackage::Parse(tlvEntity, tlv_count, parseBuf, buf_count);

 

    if(strncmp((char*)parseBuf, (char*)requestBuf, sizeof(requestBuf)) == 0)

    {

        AfxMessageBox("TRUE");

    }

    else

    {

        AfxMessageBox("FALSE");

    }

最后测试结果中,可以得到最后将弹出“TRUE”的对话框。证明构造TLV得到的TLVEntity,再对这个实体进行解析,可以的到解析后的字节数组,最后通过strncmp的方法比较判断,是否原始字节数组和解析后的字节数组是否一致。

 

另外,为了方便C#程序员的使用,我对该C++程序重新改写了一下,得出的结果也是一样的。

TLVEntity.cs

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

/// <summary>

    /// TLV实体

    /// </summary>

    public class TLVEntity

    {

        /// <summary>

        /// 标记

        /// </summary>

        public byte[] Tag { getset; }

 

        /// <summary>

        /// 数据长度

        /// </summary>

        public byte[] Length { getset; }

 

        /// <summary>

        /// 数据

        /// </summary>

        public byte[] Value { getset; }

 

        /// <summary>

        /// 标记占用字节数

        /// </summary>

        public int TagSize { getset; }

 

        /// <summary>

        /// 数据长度占用字节数

        /// </summary>

        public int LengthSize { getset; }

 

        /// <summary>

        /// 子嵌套TLV实体

        /// </summary>

        public TLVEntity Sub_TLVEntity { getset; }

    }

以及TLVPackage.cs

接着,写测试程序:

运行结果:

image

https://www.cnblogs.com/zeng-/p/4754900.html

总结

TLV在数据通信方面,其实运用得很广泛,在应用层数据通信中,如HTTP协议,HTML,XML语言本身定义了一些标签 (Body,Head,Script)对数据串行化,接收方再根据标签解析原始数据,通过浏览器展现出来。因此本质上也是属于TLV协议的设计模式。甚至 在传输层的TCP,UDP协议,你完全可以通过TLV实现自定义的应用协议。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值