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#

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

 /// <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 { return this.Tag.Length; } } /// <summary>
/// 数据长度占用字节数
/// </summary>
public int LengthSize { get { return this.Length.Length; } } /// <summary>
/// 子嵌套TLV实体列表
/// </summary>
public List<TLVEntity> SubTLVEntity { get; set; }
}

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

    /// <summary>
/// TLV格式报文打包解析
/// </summary>
public class TLVPackage
{
#region TLV 打包 /// <summary>
/// TLV报文打包
/// </summary>
/// <param name="buffer">字节数据</param>
/// <returns></returns>
public static List<TLVEntity> Construct(byte[] buffer)
{
List<TLVEntity> resultList = new List<TLVEntity>();
int currentIndex = ;
while (currentIndex < buffer.Length)
{
TLVEntity entity = new TLVEntity();
//1. 根据Tag判断数据是否是嵌套的TLV
bool hasSubEntity = HasSubEntity(buffer, currentIndex); #region Tag解析
entity.Tag = GetTag(buffer, currentIndex);
currentIndex += entity.Tag.Length;
#endregion #region Length解析
entity.Length = GetLength(buffer, currentIndex);
currentIndex += entity.Length.Length;
#endregion #region Value解析
int valueLength = GetValueLengthByLengthByteValue(entity.Length);
entity.Value = buffer.Take(currentIndex + valueLength).Skip(currentIndex).ToArray();
if (hasSubEntity)//判断是否是嵌套结构
entity.SubTLVEntity = Construct(entity.Value);//嵌套结构递归解析
currentIndex += entity.Value.Length;
#endregion resultList.Add(entity);
}
return resultList;
} /// <summary>
/// 是否存在嵌套实体
/// </summary>
/// <returns></returns>
private static bool HasSubEntity(byte[] bytes, int index)
{
if (bytes.Length < index + )
throw new ArgumentException("无效的索引值");
return (bytes[index] & 0x20) == 0x20;
} /// <summary>
/// 获取Tag字节数据
/// </summary>
/// <param name="bytes">长度</param>
/// <param name="index">索引位置</param>
/// <returns></returns>
private static byte[] GetTag(byte[] bytes, int index)
{
if (bytes.Length < index + )
throw new ArgumentException("无效的索引值");
//判断Tag所占字节长度
if ((bytes[index] & 0x1f) == 0x1f)
{//占2字节
return new byte[] { bytes[index], bytes[index + ] };
}
else
{//占1字节
return new byte[] { bytes[index] };
}
} /// <summary>
/// 获取长度
/// </summary>
/// <param name="bytes">长度</param>
/// <param name="index">索引位置</param>
/// <returns></returns>
private static byte[] GetLength(byte[] bytes, int index)
{
if (bytes.Length < index + )
throw new ArgumentException("无效的索引值");
//判断Length部分所占字节 是1个字节还是多个字节
if ((bytes[index] & 0x80) == 0x80)
{//占多个字节
int lengthSize = (bytes[index] & 0x7f) + ;//获取Length所占字节数
return bytes.Take(index + lengthSize).Skip(index).ToArray();
}
else
{//占单个字节
return new byte[] { bytes[index] };
}
}
/// <summary>
/// 根据Length部分的值获取到value部分的值
/// </summary>
/// <param name="bytes">Length部分的值</param>
/// <returns></returns>
private static int GetValueLengthByLengthByteValue(byte[] bytes)
{
int length = ;
if (bytes.Length == )
length = bytes[];
else
{
//从下一个字节开始算Length域
for (int index = ; index < bytes.Length; index++)
{
length += bytes[index] << ((index-) * ); //计算Length域的长度
}
}
return length;
} #endregion #region TLV 解析
/// <summary>
/// 解析TLV
/// </summary>
/// <param name="list">
/// <returns></returns>
public static byte[] Parse(List<TLVEntity> list)
{
byte[] buffer = new byte[];
int currentIndex = ;
int currentTLVIndex = ;
int valueSize = ; while (currentTLVIndex < list.Count())
{
valueSize = ;
TLVEntity entity = list[currentTLVIndex]; Array.Copy(entity.Tag, , buffer, currentIndex, entity.TagSize);    //解析Tag currentIndex += entity.TagSize; for (int index = ; index < entity.LengthSize; index++)
{
valueSize += entity.Length[index] << (index * ); //计算Length域的长度
}
if (valueSize > )
{
buffer[currentIndex] = Convert.ToByte(0x80 | entity.LengthSize);
currentIndex += ;
} Array.Copy(entity.Length, , buffer, currentIndex, entity.LengthSize);  //解析Length currentIndex += entity.LengthSize;
//判断是否包含子嵌套TLV
if (entity.SubTLVEntity == null)
{
Array.Copy(entity.Value, , buffer, currentIndex, valueSize);   //解析Value
currentIndex += valueSize;
}
else
{
byte[] tempBuffer = Parse(entity.SubTLVEntity);
Array.Copy(tempBuffer, , buffer, currentIndex, tempBuffer.Length); //解析子嵌套TLV
currentIndex += tempBuffer.Length;
} currentTLVIndex++;
} byte[] resultBuffer = new byte[currentIndex];
Array.Copy(buffer, , resultBuffer, , currentIndex); return resultBuffer;
}
#endregion
}

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 > )
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

EMV/PBOC解析(三) TLV格式解析(C#)的更多相关文章

  1. java拾遗3----XML解析(三) StAX PULL解析

    使用PULL方式解析XML: Pull是STAX的一个实现 StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API StA ...

  2. plist文件、NSUserDefault 对文件进行存储的类、json格式解析

    ========================== 文件操作 ========================== Δ一 .plist文件 .plist文件是一个属性字典数组的一个文件: .plis ...

  3. EMV/PBOC 解析(一) 卡片文件结构

    刚到公司老大便发我一份文档<智能卡ISO7816-4规范(中文版)>,然后就让我研究下IC智能卡数据读取和支付.身为一直做.NET开发的我对硬件啥的一无所知,各种无头绪啊,研究了两天后,稍 ...

  4. JSON格式解析和libjson使用简介(关于cjson的使用示例)

    JSON格式解析和libjson使用简介 在阅读本文之前,请先阅读下<Rss Reader实例开发之系统设计>一文. Rss Reader实例开发中,进行网络数据交换时主要使用到了两种数据 ...

  5. 转:YUV RGB 常见视频格式解析

    转: http://www.cnblogs.com/qinjunni/archive/2012/02/23/2364446.html YUV RGB 常见视频格式解析 I420是YUV格式的一种,而Y ...

  6. Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件

    Fixflow引擎解析(四)(模型) - 通过EMF扩展BPMN2.0元素 Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件 Fixflow引擎解析(二)(模型) - BPMN ...

  7. TS格式解析

    1.TS格式介绍 TS:全称为MPEG2-TS.TS即"Transport Stream"的缩写.它是分包发送的,每一个包长为188字节(还有192和204个字节的包).包的结构为 ...

  8. 虚拟机VHD格式解析到NTFS文件系统解析

    本来的需求是XEN下的镜像取证,但这篇仅包括他支持的一种格式,就是VHD,此项目从头开始大概用了两周时间,中间遇到了很多让人头大的问题,光是思考的笔记就写了十几页纸,不过实际上并没有那么难,主要是很久 ...

  9. 【转】C语言文件操作解析(三)

    原文网址:http://www.cnblogs.com/dolphin0520/archive/2011/10/07/2200454.html C语言文件操作解析(三) 在前面已经讨论了文件打开操作, ...

随机推荐

  1. Wpf 数据绑定简介、实例1

    简介:1.WPF绑定使用的源属性必须是依赖项属性,这是因为依赖项属性具有内置的更改通知支持,元素绑定表达式使用了Xaml扩展标记, WPF绑定一个控件是使用Binding.ElementName, 绑 ...

  2. ORACLE导入导出操作篇

    1. DIRECTORY 指定转储文件和日志文件所在的目录DIRECTORY=directory_objectDirectory_object用于指定目录对象名称.需要注意,目录对象是使用CREATE ...

  3. Android开发手记(19) 数据存储四 ContentProvider

    转载自:http://www.cnblogs.com/devinzhang/archive/2012/01/20/2327863.html Android为数据存储提供了五种方式: 1.SharedP ...

  4. Delphi 用ToolButton和MonthCalendar实现DateTimePicker的功能

    效果图如下: 实现平台:xp xe2,其中以上功能的实现,核心主要是参考了万一老师的资料,连接:http://www.cnblogs.com/del/archive/2011/05/12/204411 ...

  5. ThinkPHP 的CURD 基本操作

    说起CURD,懂点SQL的人都知道,就是增删改查,做业务系统的时候,往往离不开这CURD,最近也是刚刚接触ThinkPHP,ThinkPHP的灵活性是比原生PHP好用的多,下面我就简单的介绍一下我的学 ...

  6. JavaScript 框架比较

    显著增强 JavaScript 开发的框架概览 Joe Lennon, 软件开发人员, 自由职业者 简介: 现代 Web 站点和 Web 应用程序倾向于依赖大量客户端 JavaScript 来提供丰富 ...

  7. ubuntu忘记登录账户以及密码

    笔者在诸多方面仍然是初学者.感兴趣的方面也很多,电脑装上ubuntu14.04也有一段时间了,但仍然在不断学习更多基础的东西. 因为对于命令行界面还有些不习惯,所以一直依赖于图形界面,需要使用终端的时 ...

  8. underscorejs-sortBy学习

    2.17 sortBy 2.17.1 语法 _.sortBy(list, iteratee, [context]) 2.17.2 说明 返回一个排序后的list拷贝副本. list为集合,如数组.对象 ...

  9. postgres常用类型

    数值类型 名字 存储空间 描述 范围 smallint 2 字节 小范围整数 -32768 到 +32767 integer 4 字节 常用的整数 -2147483648 到 +2147483647 ...

  10. 30 个 Python 语言的特点技巧

    1   介绍 从我开始学习Python时我就决定维护一个经常使用的“窍门”列表.不论何时当我看到一段让我觉得“酷,这样也行!”的代码时(在一个例子中.在StackOverflow.在开源码软件中,等等 ...