前言

隔离上篇文章IC卡(智能卡)APDU通讯总结太久了,这次整理一下TLV数据解析的教程,供大家参考。有时候发送指令读取到IC卡数据,直接转 ASCII码就可以拿到自己想要的数据,和业务交互。但是银行卡读取到的报文数据了,直接转是行不通的。本文重点是数据解析,不是讲解与卡怎么发送指令通讯,发送什么指令(这种都是大同小异,遵循中国金融集成电路(IC)卡规范)。

TLV

PBOC(The People’s Bank of China 中国人民银行)的银行IC卡大部分数据都是 TLV(tag-length-value) 格式的(或者叫IC卡55域数据)。 TLV(BER(Basic Encoding Rules)是 ASN.1 中最早定义的编码规则,BER 传输语法的格式一直是 TLV 三元组) 是 tag, length 和 value 的缩写,tag是这个数据元的标示,length是这个数据元值的部分的长度,value则是该数据元的值。

Tag(标签)

注:字节排序方向为从左往右数,第一个字节即为最左边的字节。bit排序规则同理。

可以用下面一张图表示:

tag在pboc中最多占两个字节,第一个字节的编码规则 。
b7 和 b6 两位标识 tag 所属类别 。00表示TLV描述的是基本数据类型(Primitive Frame, int,string,long…),01表示用户自定义类型(Private Frame,常用于描述协议中的消息
b5 决定当前的 TLV 数据是一个单一的数据(Primitive Data 编码)和复合结构的数据(Constructed Data编码) 。 复合的 TLV 是指 value 域里也包含一个或多个 TLV, 类似嵌套的编码格式 。

b4~b0 如果全为 1 ,则说明这个 tag下面还有一个子字节,占两个字节,否则tag占一个字节 。如果tag占用两个字节,第二个字节的编码格式, b7决定tag是否还有后绪的字节存在,bit7为1时存在后续字节,为0时不存在后续字节。bit6~bit0:Tag正文

举例说明:

  • 9F33(‭9F:1001 1111‬):为一个占用两个字节的tag标签。
  • 95(‭1001 0101‬):为一个占用一个字节的tag标签。

Length(长度)

length占1~3个字节长度,描述Value部分所占字节的个数,编码格式分两类:定长方式(DefiniteForm)和不定长方式(IndefiniteForm),其中定长方式又包括短形式与长形式。

定长方式

短形式: 字节第7位为0(最左边字节的最左bit位(即bit7为0)),表示Length使用1个字节即可满足Value类型长度的描述,它的后续7个bit位(即bit6~bit0)表示Value取值长度的范围在0~127之间的,把后续7bit转成十进制值即可表示Value的字节长度。

举例说明:

03(0000 0011):表示Value占用三个字节,所以,若Value的长度在1~127字节之间,那么该L字段本身仅占一个字节

长形式: 字节第7位为1(最左边字节的最左bit位(即bit7为1)),表示Length使用多个字节描述Value类型长度,Value类型的长度大于127时,bit6~bit0用来描述Length值占用的字节数,把bit6~bit0转成十进制值即可表示Value的字节长度。

举例说明:

81FF(1000 0001 1111 1111):1000 0001表示Value为长形式,占用一个字节,其Value的字节数为1111 1111即255个字节

所以,若Value的长度在128~255字节之间,那么该L字段本身**仅占两个字节 **

不定长方式

Length所在八位组固定编码为0x80,但在Value编码结束后以两个0x00结尾。这种方式使得可以在编码没有完全结束的情况下,可以先发送部分数据给对方。

举例说明:
1000 000 … 00:1000 000表示Value长度,value最后的两位是00

Value(数值)

由一个或多个值组成 ,值可以是一个原始数据类型(Primitive Data),也可以是一个TLV结构(Constructed Data)

单一结构(原始数据类型):T L V
复合结构(复合TLV结构):T L V(嵌套一个以上TLV)

示例解析说明

上面解释了TLV的组成原理,下面举例进行分析,最后附上解析代码。上面说了TLV数据格式有可能TLV嵌套TLV,编码解析使用递归解析。

下面是一个完整的TLV数据格式(复合结构,单一结构比较简单,不做举例说明),APDU正常通讯成功后,取返回数据Data和SW1 SW2,不记得APDU通讯数据格式请看IC卡(智能卡)APDU通讯总结

704D5A0A6221871000001018326F8E0C000000000000000002031F009F0D05D86004A8009F0E0500109800009F0F05D86804F8005F24032608315F280201569F0702FF005F25031608239F080200309000

9000: 正常 成功执行
704D5A0A6221871000001018326F8E0C000000000000000002031F009F0D05D86004A8009F0E0500109800009F0F05D86804F8005F24032608315F280201569F0702FF005F25031608239F08020030:要解析的数据

为方便观察,整理成表格,请点击查看,完整解析如下图:

代码实现TLV解析

基于上面TLV组成原理编码实现如下:

核心解码类MyTlvDecode

public class MyTlvDecode {/*** 递归解析TLV格式数据** @param tlvHexStr* @param tlvs*/
public void decodeTLV(String tlvHexStr, List<TLV> tlvs) {if (tlvHexStr == null || tlvHexStr.length() == 0) {throw new NullPointerException("tlvHexStr 为null.");}if (tlvHexStr.length() % 2 != 0) {throw new IllegalArgumentException("tlvHexStr 参数非法.");}byte[] bytes = ByteUtils.hexStringToByteArr(tlvHexStr);decodeTLV(bytes, bytes.length, tlvs);
}/*** 递归解析TLV格式数据** @param tlvBytes* @param len* @param tlvs*/
public void decodeTLV(byte[] tlvBytes, int len, List<TLV> tlvs) {if (tlvBytes == null || tlvBytes.length == 0) {throw new NullPointerException("tlvBytes 为null.");}System.out.println(ByteUtils.byteArrToHexString(tlvBytes));byte[] tag;int vLength;int lLength;boolean complexTag = false;//bit与运算同为1才为1,0x20=00100000, b5=1是判断单一结构还是复合结构if ((tlvBytes[0] & 0x20) != 0x20) { // 单一结构if ((tlvBytes[0] & 0x1f) != 0x1f) { // tag占用一个字节,否则占用两个字节(b0-b5都是1)vLength = tlvBytes[1] == 0x81 ? tlvBytes[2] : tlvBytes[1];lLength = 2;//不定长方式if (vLength > 0x80) {lLength = 3;//定长方式}tag = new byte[1];} else {// tag为两个字节vLength = tlvBytes[2] == 0x81 ? tlvBytes[3] : tlvBytes[2];lLength = 3;if (vLength > 0x80) {lLength = 4;}tag = new byte[2];}if (vLength < 0) {throw new RuntimeException("TLV解码异常, vLength:" + vLength);}} else { // 复合结构complexTag = true;if ((tlvBytes[0] & 0x1f) != 0x1f) { // tag为一个字节vLength = tlvBytes[1] == 0x81 ? tlvBytes[2] : tlvBytes[1];lLength = 2;if (vLength > 0x80) {lLength = 3;}tag = new byte[1];} else {// tag为两个字节vLength = tlvBytes[2] == 0x81 ? tlvBytes[3] : tlvBytes[2];lLength = 3;if (vLength > 0x80) {lLength = 4;}tag = new byte[2];}if (vLength < 0) {throw new RuntimeException("TLV解码异常,tag:" + tag + ",vLength:" + vLength);}}//分别解析出T、L、VSystem.arraycopy(tlvBytes, 0, tag, 0, tag.length);byte[] value = new byte[vLength];System.arraycopy(tlvBytes, lLength, value, 0, value.length);String tagStr = ByteUtils.byteArrToHexString(tag);String tagValue = ByteUtils.byteArrToHexString(value);int tagLength = value.length;tlvs.add(new TLV(tagStr, tagLength, tagValue));if (complexTag){decodeTLV(value, tagLength, tlvs);}if (len > vLength + lLength) {byte[] nextTlv = new byte[len - (vLength + lLength)];System.arraycopy(tlvBytes, vLength + lLength, nextTlv, 0, nextTlv.length);decodeTLV(nextTlv, nextTlv.length, tlvs);}
}/*** @param args 测试程序*/public static void main(String[] args) {MyTlvDecode t = new MyTlvDecode();List<String> tlvArr = new ArrayList<>();tlvArr.add("704D5A0A6221871000001018326F8E0C000000000000000002031F009F0D05D86004A8009F0E0500109800009F0F05D86804F8005F24032608315F280201569F0702FF005F25031608239F080200309000");for (int i = 0; i < tlvArr.size(); i++) {List<TLV> tlvs = new LinkedList<>();long start = System.currentTimeMillis();byte[] bytes = ByteUtils.hexStringToByteArr(tlvArr.get(i));t.decodeTLV(bytes, bytes.length, tlvs);//        t.decodeTLV(tlvStr, tlvs);long end = System.currentTimeMillis();System.out.println("解析耗时:" + (end - start) + "ms");for (TLV tlv : tlvs) {System.out.println("tag:" + tlv.tag + ",length:" + tlv.length + ",value:" + tlv.value);}System.out.println("\n");}}}

实体TLV:

public class TLV {/** 标签 */public String tag;/** 长度 */public int length;/** 值 */public String value;public TLV(){}public TLV(String tag, int length, String value) {this.length = length;this.tag = tag;this.value = value;}//...}

运行结果如下:

解析耗时:1ms
tag:70,length:77,value:5A0A6221871000001018326F8E0C000000000000000002031F009F0D05D86004A8009F0E0500109800009F0F05D86804F8005F24032608315F280201569F0702FF005F25031608239F08020030
tag:5A,length:10,value:6221871000001018326F
tag:8E,length:12,value:000000000000000002031F00
tag:9F0D,length:5,value:D86004A800
tag:9F0E,length:5,value:0010980000
tag:9F0F,length:5,value:D86804F800
tag:5F24,length:3,value:260831
tag:5F28,length:2,value:0156
tag:9F07,length:2,value:FF00
tag:5F25,length:3,value:160823
tag:9F08,length:2,value:0030

然后对比文档就知道上面tag的含义了,上图表格中已经标标出

小结

本文主要介绍TLV的组成原理和解析方法,解析不保证100%通用,但适合大部分业务需求。TLV数据解析这种不难,多读几遍和实践几次,就可以熟能生巧。

IC卡(银行卡)APDU数据格式TLV解析相关推荐

  1. web调用IC卡读卡器开发第六章--CPU卡APDU操作

    CPU卡APDU的web操作 CPU卡介绍 友我科技IC卡读卡器web插件 CPU卡复位 CPU卡APDU操作 操作例程 CPU卡介绍 CPU卡与M1卡不同, 他不是一种逻辑存储卡,一张空白的CPU卡 ...

  2. IC卡(M1卡)梯控日期的算法解析和计算

    在IC卡分析工具的帮助下,我把常见的一些梯控日期的算法进行了分析和推导,总结出了IC卡梯控日期的算法规则,具体内容如下表所示: 序号 位数 明暗 方法 梯控 算法(以2099年12月31日为例) 十六 ...

  3. 银联IC卡读卡流程详解--读卡器与卡交互指令

    最近因研究了下银联借记/贷记应用卡片规范,发现网上可参考资源较少,于是萌生了写下这篇文字的想法,希望可以帮助到有需要的兄弟姐妹,有描述不清晰或者有错误的地方欢迎指正. 下面进入正题,测试使用的卡是招商 ...

  4. 商业银行金融IC卡国密改造安全建设方案

    一.背景 金融和重要领域国密改造是我国大力推进自主国产密码应用的重要政策.2011年3月,人民银行发布了<关于推进金融IC卡应用工作的意见>,开始全面启动我国金融IC卡迁移,要求2015年 ...

  5. 接触式IC卡 - STM32(Smart Card)

    目录 1.复位 1.1.冷复位 1.2.热复位 2.Smart Card功能 2.1.初始配置 2.2.波特率计算 2.3.IO收发代码 2.4.IC通讯测试 接触IC的硬件通讯其实不难理解,上层逻辑 ...

  6. 中国金融集成电路(IC)卡规范

    转载自:https://blog.csdn.net/supergame111/article/details/33730809 一.中国金融IC卡规范发展历程 金融IC卡是采用集成电路(IC)技术和金 ...

  7. Proxmark3教程1:小白如何用PM3破解复制M1全加密门禁IC卡

    IC卡已经在我们的生活中无处不在了,门禁,电梯,吃饭,洗车,可以说与我们的生活息息相关了. 但是如果有一天,你的门禁卡丢了,怎么配呢?跟配钥匙一样的,必须现有原钥匙才可以. 那我们今天就看看,如何用P ...

  8. ID卡、IC卡,RFID卡,NFC卡的简单介绍

    ID卡与IC卡的区别 ID卡仅仅记录卡号,卡内的卡号读取无任何权限,易于仿制.ID卡不可写入数据,其记录内容(卡号)只可由芯片生产厂一次性写入,开发商只可读出卡号加以利用,无法根据系统的实际需要制订新 ...

  9. 认证中心公钥(CA)与IC卡参数

    认证中心公钥参数共包含如下参数信息:RID.认证中心公钥索引.认证中心公钥有效期.认证中心公钥哈什算法标识.认证中心公钥算法标识.认证中心公钥模.认证中心公钥指数.认证中心公钥校验值. 下面是对各参数 ...

最新文章

  1. 干货 | 漫谈图神经网络
  2. IIS启动时出现0x8ffe2740的错误
  3. JavaScript代码(一)
  4. svn + apache of linux 安装配置
  5. 实验四:xl命令的常见子命令以及操作
  6. python元组与列表的区别、简答题_细解python面试题(一)元组和列表的区别
  7. java模拟双向链表实现
  8. YYYY-mm-dd HH:MM:SS大小写解释
  9. 2021零基础学习人工智能(AI)思想篇
  10. 网站托管收费是否有标准
  11. Flutter 中由 BuildContext 引发的血案
  12. 【大数据开发】SparkSQL——RDD、DataFrame、DataSet相互转换、DSL常用方法、SQL风格语法、Spark读写操作、获取Column对象的方式
  13. JSON 字符串是如何被解析的?JsonParser 了解一下
  14. Android计算器(计算表达式,能计算小数点以及括号)方法简单易懂
  15. BZOJ 1616: [Usaco2008 Mar]Cow Travelling游荡的奶牛【DP】
  16. Spring 编程式事务实例
  17. Android硬件控制之USB连接
  18. 超市信息管理程序c语言购物车,c语言购物车代码
  19. QT 使用数据流方式QDataSteam读写结构体数据
  20. 中国交通物流行业规模预测及未来发展趋势分析报告2021-2027年

热门文章

  1. 教你自动化测试执行用例报告
  2. Java爬虫之下载全世界国家的国旗图片
  3. 如何下载龙潭街道卫星地图高清版大图
  4. RF3401M蓝牙对讲无线音频发射接收串口数据透传芯片模块方案--KT3401B芯片
  5. BT宝塔面板免费使用专业版网站监控报表插件
  6. WiFi共享精灵=免费wifi+……
  7. IBM V系列存储常用命令
  8. 彻底解决git clone速度慢的问题(ubuntu)
  9. 【Docker】clair镜像扫描的实现
  10. 计算机知识绘画素材,计算机基础知识及素材.doc