赞
踩
隔离上篇文章IC卡(智能卡)APDU通讯总结太久了,这次整理一下TLV数据解析的教程,供大家参考。有时候发送指令读取到IC卡数据,直接转 ASCII码就可以拿到自己想要的数据,和业务交互。但是银行卡读取到的报文数据了,直接转是行不通的。本文重点是数据解析,不是讲解与卡怎么发送指令通讯,发送什么指令(这种都是大同小异,遵循中国金融集成电路(IC)卡规范)。
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正文
举例说明:
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组成原理编码实现如下:
核心解码类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、V System.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数据解析这种不难,多读几遍和实践几次,就可以熟能生巧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。