PBOC的IC卡大部分資料都是 TLV 格式的, TLV 是 tag, length 和 value 的縮寫,tag是這個資料元的标示,length是這個資料元值的部分的長度,value則是該資料元的值。其中tag在pboc中最多占兩個位元組,第一個位元組的編碼規則 。 b8 和 b7 兩位辨別 tag 所屬類别 。 這個可以暫時不用理 。b6 決定目前的 TLV 資料是一個單一的資料和複合結構的資料 。 複合的 TLV 是指 value 域裡也包含一個或多個 TLV, 類似嵌套的編碼格式 . b5~b1 如果全為 1 ,則說明這個 tag 下面還有一個子位元組 , 占兩個位元組 , 否則 tag 占一個位元組 。如果tag占用兩個位元組,第二個位元組的編碼格式, B8決定tag是否還有後緒的位元組存在,因為前面說過,PBOC/EMV裡的tag最多占兩個位元組,是以該位保持為0。
下面是解析TLV格式的java代碼:
import java.util.HashMap;
import java.util.Map;
public class TLVDecode {
Map<String, String> tlvMap = new HashMap<String, String>();
/*
* 遞歸解析TLV格式資料
* xuys
* 2014-3-2
*/
public void decodeTLV(String srcTLV, int len) {
if (srcTLV.substring(0, srcTLV.length()).length() > 0) {
String[] st = new String[srcTLV.substring(0, srcTLV.length())
.length() / 2];
for (int t = 0; t < srcTLV.substring(0, srcTLV.length()).length() / 2; t++) {
st[t] = srcTLV.substring(0, srcTLV.length()).substring(2 * t,
2 * t + 2);
}
if ((Integer.valueOf(st[0], 16) & 0x20) != 0x20) { // 單一結構
if ((Integer.parseInt(st[0], 16) & 0x1f) != 0x1f) { // tag為一個位元組
StringBuilder sbd = new StringBuilder();
int dataLen = Integer.valueOf(st[1], 16) == 0x81 ? Integer
.valueOf(st[2], 16) : Integer.valueOf(st[1], 16);
int dt = 2;
if (dataLen > 0x80) {
dt = 3;
}
for (int t = dt; t < dataLen + dt; t++) {
sbd.append(st[t]);
}
tlvMap.put(st[0], sbd.toString());
if (len > dataLen + dt) {
StringBuilder lastTlv = new StringBuilder();
for (int t = dataLen + dt; t < st.length; t++) {
lastTlv.append(st[t]);
}
decodeTLV(lastTlv.toString(), st.length
- (dataLen + dt));
}
} else {// tag為兩個位元組
StringBuilder sbd = new StringBuilder();
int dataLen = Integer.valueOf(st[2], 16) == 0x81 ? Integer
.valueOf(st[3], 16) : Integer.valueOf(st[2], 16);
int dt = 3;
if (dataLen > 0x80) {
dt = 4;
}
for (int t = dt; t < dataLen + dt; t++) {
sbd.append(st[t]);
}
tlvMap.put(st[0] + st[1], sbd.toString());
if (len > dataLen + dt) {
StringBuilder lastTlv = new StringBuilder();
for (int t = dataLen + dt; t < st.length; t++) {
lastTlv.append(st[t]);
}
decodeTLV(lastTlv.toString(), st.length
- (dataLen + dt));
}
}
} else { // 複合結構
if ((Integer.valueOf(st[0], 16) & 0x1f) != 0x1f) { // tag為一個位元組
StringBuilder sbd = new StringBuilder();
int dataLen = Integer.valueOf(st[1], 16) == 0x81 ? Integer
.valueOf(st[2], 16) : Integer.valueOf(st[1], 16);
int dt = 2;
if (dataLen > 0x80) {
dt = 3;
}
for (int t = dt; t < dataLen + dt; t++) {
sbd.append(st[t]);
}
tlvMap.put(st[0], sbd.toString());
decodeTLV(sbd.toString(), sbd.length() / 2);
if (len > dataLen + dt) {
StringBuilder lastTlv = new StringBuilder();
for (int t = dataLen + dt; t < st.length; t++) {
lastTlv.append(st[t]);
}
decodeTLV(lastTlv.toString(), st.length);
}
} else {// tag為兩個位元組
StringBuilder sbd = new StringBuilder();
// int tk = Integer.valueOf(st[2], 16);
int dataLen = Integer.valueOf(st[2], 16) == 0x81 ? Integer
.valueOf(st[3], 16) : Integer.valueOf(st[2], 16);
int dt = 3;
if (dataLen > 0x80) {
dt = 4;
}
for (int t = dt; t < dataLen + dt; t++) {
sbd.append(st[t]);
}
tlvMap.put(st[0] + st[1], sbd.toString());
decodeTLV(sbd.toString(), sbd.length() / 2);
if (len > dataLen + 4) {
StringBuilder lastTlv = new StringBuilder();
for (int t = dataLen + dt; t < st.length; t++) {
lastTlv.append(st[t]);
}
decodeTLV(lastTlv.toString(), st.length);
}
}
}
}
}
public Map<String, String> getTlvMap() {
return tlvMap;
}
public void setTlvMap(Map<String, String> tlvMap) {
this.tlvMap = tlvMap;
}
/**
* @param args
* 測試程式
*/
public static void main(String[] args) {
TLVDecode t = new TLVDecode();
t.decodeTLV(
"7081E09081B04174C908D9EFBB0F8978BF8C964C22F9ACAA03C5FAE97AC8DFB829C3320E22A4BF45A8DF8010A2B81B3770F801BA1F4FEBA72195FFBBAA48274DB0B44903709FF49D9BD4F414938E32C21419A2E877E08D1E21F8DA7725860CAF3D98FB61D3F5C2B95A9BE870741EA85DE4E82A1BEDEA2BC175AE4927A26817277BA0674198500D176A779641CEAE4BB9E25039A3AAC448BCDD7781238D7882A86DCCB8CB913820494C1BB9574201DEF582EC0404D65A9F320103922420CF86C702825B7A3DD811C5F0F61D6EED533458FEFE81C931AB58910BE9A2033E85C1918F0103",
"7081E09081B04174C908D9EFBB0F8978BF8C964C22F9ACAA03C5FAE97AC8DFB829C3320E22A4BF45A8DF8010A2B81B3770F801BA1F4FEBA72195FFBBAA48274DB0B44903709FF49D9BD4F414938E32C21419A2E877E08D1E21F8DA7725860CAF3D98FB61D3F5C2B95A9BE870741EA85DE4E82A1BEDEA2BC175AE4927A26817277BA0674198500D176A779641CEAE4BB9E25039A3AAC448BCDD7781238D7882A86DCCB8CB913820494C1BB9574201DEF582EC0404D65A9F320103922420CF86C702825B7A3DD811C5F0F61D6EED533458FEFE81C931AB58910BE9A2033E85C1918F0103"
.length() / 2);
for (String tt : t.tlvMap.keySet()) {
System.out.println(tt + " : " + t.tlvMap.get(tt));
}
}
}