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

C#实现TLV编解码算法(包含完整源代码) EncodeLength方法接收一个长度参数,返回一个字节数组,表示编码后的长度。如果长度大于等于128个字节,需要使用多个字节表示长度,其中第一个字节表示占用的字节长度,剩下的字节表示具体长度。这个例子首先定义了一个包含多个TLV编码的数据,分别解码其中的每个TLV编码,并打印出Tag、Length和Value的信息。如果第一个字节的最高位为1,表示Length字段使用多个字节表示,需要一直向后查找,直到找到最后一个字节为止。DecodeTag方法将字节数组转换为整型,并反转字节序,得到真正的Tag值。 阅读详情

金融系统中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 { get; set; }

 

        /// <summary>

        /// 数据长度

        /// </summary>

        public byte[] Length { get; set; }

 

        /// <summary>

        /// 数据

        /// </summary>

        public byte[] Value { get; set; }

 

        /// <summary>

        /// 标记占用字节数

        /// </summary>

        public int TagSize { get; set; }

 

        /// <summary>

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

        /// </summary>

        public int LengthSize { get; set; }

 

        /// <summary>

        /// 子嵌套TLV实体

        /// </summary>

        public TLVEntity Sub_TLVEntity { get; set; }

    }

以及TLVPackage.cs

接着,写测试程序:

运行结果:

image

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

总结

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

 

C#: 实现PBOC/EMV的TLV算法 在这个示例中,第一个TLV对象的Tag是9F26,Length是08,Value是4F0F459F2701。TLV(Tag-Length-Value)是一种数据编码格式,广泛应用于金融领域的支付卡行业,其中PBOC(中国银联标准)和EMV(国际支付标准)是最常见的应用。通过上述代码,我们实现了一个简单的TLV解析器和构建器,以处理PBOC/EMV格式的TLV数据。接下来,我们可以编写一个TLV解析器的方法,该方法接受一个TLV字符串作为输入,并返回一个TLV对象的列表。 阅读详情

相关推荐

C++ TLV解析器源代码

C++ TLV解析器源代码 网上搜索了很多类似代码,没有一个符合我的要求。 ver3 非模板类,已经比较好用了。 ver3 模板类,目前还有一些问题,对字节序的处理待完善。

IC卡(银行卡)APDU数据格式TLV解析

IC卡TLV数据解析

Kelly 5953

C++ 与金融计算

C++与金融衍生品模型的构造与计算,其实就是C++的与金融衍生品有关的类的编写

PBOC命令格式解析1

select选择命令 选择(SELECT)命令通过文件名或AID来选择IC卡中的PSE或ADF。 成功执行该命令设定PSE或ADF的路径。后续命令作用于与用SFI选定的PSE或ADF相联系的AEF。 从IC卡返回的响应报文包含回送FCI。 GPO 交互 获取处理选项(GPO)命令用来启动IC卡内的交易。 IC卡的响应报文中包含应用交互特征(AIP)和应用文件定位器(AFL)...

zhuimenglushang的博客 1350

C++实现tlv编解码

c++实现tlv的编解码,使用multimap封装tlv容器,接口与stl标准容器保持一致,可读性和扩展性较好。

TLV解析算法

银联标准PBOC3.0,TLV解析C语言算法,解析出Tag length Value

TLV查询分析器

用于分析、查看QPBOC中TLV标签的解析和查询。

PBOC3.0 TLV 小工具

该工具实现PBOC30的TLV的解析功能,同时可以查询PBOC30中定义的TAG,对于金融PBOC30相关开发的人员来说是非常有用的工具

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

http://www.cnblogs.com/liping13599168/archive/2011/06/15/2081366.html

gszhy的专栏 2436

如何点亮一颗sensor

要点亮一个sensor需要从,时钟、sensorI2C,供电,reset脚,mipi的配置几个方面一一确认。 reset脚拉高 给sensor的时钟有没有。(目前用到的常为24MHZ) 供电 查看sensor的数据手册,看有几个电源,用示波器量一下是否为手册规定的电压 比如:DVDD 数字电压: AVDD模拟电压 DOVDD芯片引脚出来的电压等为多少。 sensor的I2C能否抓到 将I2C的口对应上,用示波器量取i2c的波形。 检查有应答并且数据正确。读写地址是否正...

weixin_40787463的博客 3648

3D Gaussian Splatting环境配置和运行

使用conda命令根据environment.yml文件创建虚拟环境时,安装的pytorch是使用conda install 命令来安装的,实际上并不能成功安装带有gpu的pytorch,而这两个模块的安装依赖于带gpu的pytorch。如果不注释掉这三行,直接安装会报错

m0_46360757的博客 3326
上一篇: TLG
下一篇: TAG
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值