TLV数据解析的函数整理(Java )
如有问题,请拍板砖!!会及时更正!!
废话不多说直接上代码!!!
测试调用:
public class test {
public static void main(String[] args){
//测试数据
String data = "950500000008004F08A0000003330101019F36022935500A50424F432044454249549F3704CC8DEE7A82027C009F34030000009F101307020103A00000010A010000017400116766E99F3303E0E1C0";
TLVTools tlvTools = new TLVTools();
TLV tlv;
tlvTools.unpack(data);
for(int i=0;i<tlvTools.tlvList.size();i++)
{
tlv = tlvTools.tlvList.get(i);
System.out.print(" 标签:["+i+"]"+"\n");
System.out.print(" tag:"+tlv.getTag()+"\n");
System.out.print(" len:"+tlv.getLen()+"\n");
System.out.print("value:"+ tlvTools.bytesToHex(tlv.getValue())+"\n");
}
}
}
TLV数据类:
public class TLV{
int tag;//tag定义成数字是为了方便遍历使用
int len;
byte[] value;
public int getTag() {
return tag;
}
public void setTag(int tag) {
this.tag = tag;
}
public int getLen() {
return len;
}
public void setLen(int len) {
this.len = len;
}
public byte[] getValue() {
return value;
}
public void setValue(byte[] value) {
this.value = value;
}
}
解析 工具类:
public class TLVTools {
public List<TLV> tlvList;
public TLVTools() {
tlvList = new ArrayList<TLV>();
}
public List<TLV> unpack(String tlv){
int current = 0;//遍历数据标签
int lenValue = 0;//L的值
int tagLen = 0;//tag的长度
//将string数据转换成byte
byte[] data =hexStrToBytes(tlv);
while(current < data.length){
TLV tlvData = new TLV();
//获取tag的长度
tagLen = getTagLen(data, current);
//获取tag值,并转成int
tlvData.setTag(getTagToInt(data,current,tagLen));
current += tagLen;
//当L字段最左边字节的最左bit位(即bit8)为0,表示该L字段占一个字节
//当L字段最左边字节的最左bit位(即bit8)为1,表示该L字段不止占一个字节
//若最左字节为10000010,表示L字段除该字节外,后面还有两个字节
//若最左字节为10000001,表示L字段除该字节外,后面还有一个字节
//例如,若L字段为“1000 0001 1111 1111”,表示该子域取值占255个字节
if((data[current]&0x80) == 0x80){
int tmpLen = data[current]&0x7F;
switch(tmpLen){
case 1:
//L字段占俩个字节
lenValue = data[current + 1]&0xFF;
break;
case 2:
//L字段占三个字节
lenValue = (data[current + 1]<<8)&0xFF00 + (data[current+2]&0xFF);
break;
}
current += tmpLen + 1;
} else{
//L字段占一个字节
lenValue = data[current]&0xFF;
current +=1;
}
//设置len值
tlvData.setLen(lenValue);
//设置value值
byte[] temp = new byte[lenValue];
System.arraycopy(data, current, temp, 0, lenValue);
tlvData.setValue(temp);
current += lenValue;
tlvList.add(tlvData);
}
return tlvList;
}
//hex数据转换成byte
public byte[] hexStrToBytes(String data) {
if (data == null || "".equals(data)) {
return null;
}
int len = data.length() / 2;
byte[] result = new byte[len];
//将每个char字符单独转换成byte数据
char[] chArr = data.toCharArray();
for (int i = 0; i < len; i++) {
int pos = i * 2;
result[i] = (byte) (toByte(chArr[pos]) << 4 | toByte(chArr[pos + 1]));
}
return result;
}
//获取tag长度
public int getTagLen(byte[] data, int flag) {
/*若tag标签的第一个字节(注:字节排序方向为从左往右数,
* 第一个字节即为最左边的字节。bit排序规则同理。)的后
* 四个bit为“1111”,则说明该tag占两个字节,例如“9F33”;
* 否则占一个字节,例如“95”。*/
int tagLen = 1;
for (int i = 0; i < 2; i++) {
byte b = data[i + flag];
if ((b & 0x0F) == 0x0F) {
tagLen++;
} else {
break;
}
}
return tagLen;
}
public static byte toByte(char c) {
byte b = (byte) "0123456789ABCDEF".indexOf(c);
return b;
}
//获取tag,并转成int
public int getTagToInt(byte[] data, int flag, int len){
int mask = 0xFF;
int temp = 0;
int result = 0;
len = Math.min(len, 4);//tag最长4位
for (int i = 0; i < len; i++) {
result <<= 8;//向左位移8
temp = data[flag + i] & mask;
result |= temp;//将获取出的数据填充到result让出右侧的8位上
}
return result;
}
private static char[] HEX_VOCABLE = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
//bytes数组转换成string
public static String bytesToHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b : data) {
//获取高4位的数据值,正好对应HEX_VOCABLE数组中的下标,和相应char的值
int high = (b >> 4) & 0x0f;
//获取低4位的数据值,正好对应HEX_VOCABLE数组中的下标,和相应char的值
int low = b & 0x0f;
sb.append(HEX_VOCABLE[high]);
sb.append(HEX_VOCABLE[low]);
}
return sb.toString();
}
}
显示结果:
标签:[0]
tag:149
len:5
value:0000000800
标签:[1]
tag:20232
len:5
value:027C009F34
标签:[2]
tag:3
len:0
value:
标签:[3]
tag:0
len:0
value:
标签:[4]
tag:40720
len:19
value:07020103A00000010A010000017400116766E9
标签:[5]
tag:40755
len:3
value:E0E1C0
附银联相关TLV说明:
本域将根据不同的交易种类包含不同的子域。处理中心仅在受理方和发卡方之间传递这些适用于IC卡交易的特有数据,而不对它们进行任何修改和处理。为适应该子域需要不断变化的情况,本域采用TLV(tag-length-value)的表示方式,即每个子域由tag标签(T),子域取值的长度(L)和子域取值(V)构成。
tag标签的属性为bit,由16进制表示,占1~2个字节长度。例如,“9F33”为一个占用两个字节的tag标签。而“95”为一个占用一个字节的tag标签。若tag标签的第一个字节(注:字节排序方向为从左往右数,第一个字节即为最左边的字节。bit排序规则同理。)的后四个bit为“1111”,则说明该tag占两个字节,例如“9F33”;否则占一个字节,例如“95”。
子域长度(即L本身)的属性也为bit,占1~3个字节长度。具体编码规则如下:
a) 当L字段最左边字节的最左bit位(即bit8)为0,表示该L字段占一个字节,它的后续7个bit位(即bit7~bit1)表示子域取值的长度,采用二进制数表示子域取值长度的十进制数。例如,某个域取值占3个字节,那么其子域取值长度表示为“00000011”。所以,若子域取值的长度在1~127字节之间,那么该L字段本身仅占一个字节。
b) 当L字段最左边字节的最左bit位(即bit8)为1,表示该L字段不止占一个字节,那么它到底占几个字节由该最左字节的后续7个bit位(即bit7~bit1)的十进制取值表示。例如,若最左字节为10000010,表示L字段除该字节外,后面还有两个字节。其后续字节的十进制取值表示子域取值的长度。例如,若L字段为“1000 0001 1111 1111”,表示该子域取值占255个字节。所以,若子域取值的长度在127~255字节之间,那么该L字段本身需占两个字节。
子域取值根据不同的子域含义分别取不同的数值。由于该域中所包含的子域都是IC卡卡片和IC卡终端所特有的信息,而非处理中心的特征信息,处理中心仅为一个数据传递的桥梁,所以具体取值需参见IC卡卡片及IC卡终端规范,并根据其标准的变化不断更新。对于境内交易需参见《中国金融集成电路(IC)卡规范 第5部分 借记/贷记卡片规范》和《中国金融集成电路(IC)卡规范 第6部分 借记/贷记终端规范分》。对于外卡收单交易需根据不同国际信用卡公司组织(例如,Visa、万事达、JCB、大莱、运通)的发卡策略参见其相应的卡片和终端规范。但由于所有组织(包括银联)对卡片和终端的定义都是基于EMV2000标准的,因此不论这些信息的具体取值有何不同,它们的tag标签都是一致的。因此,在本标准中仅给出tag标签,入网机构即可根据标签取值查找到不同组织对该子域的具体取值。下面将列举每个子域的tag标签、和长度值及其属性。
本文整理了Java中解析TLV数据的函数,包括TLV数据类和解析工具类的代码示例,以及展示了标签、长度和值的解析结果。同时,解释了银联TLV数据格式的详细规则,如tag标签的属性和子域长度的编码规则。
663





