EMV TLV解析有时不显示构造值(childs)
EMV TLV parsing sometimes does not display constructed values (childs)
我的方法 parseTLV()
有时不能正确地降级为构造值(子 TLV)。我使用过滤器 ((tag & 0x20)! = 0)
来检测构造值,但有时它无法按预期工作。
例如
70178C159F02069F03069F1A0295055F2A029A039C019F3704
导致 parseTLV
的以下结果:
- 标记:
70
- 值:8C159F02069F03069F1A0295055F2A029A039C019F3704
- 标记:
8C
- 值:9F02069F03069F1A0295055F2A029A039C019F3704
不过,我希望得到
- 标记:
70
- 值:8C159F02069F03069F1A0295055F2A029A039C019F3704
- 标记:
8C
- 值:9F02069F03069F1A0295055F2A029A039C019F3704
- 标记:
9F02
- 值:06
- 标记:
9F03
- 值:06
- 标记:
9F1A
- 值:02
- 标记:
95
- 值:05
- 标记:
5F2A
- 值:02
- 标记:
9A
- 值:03
- 标记:
9C
- 值:01
- 标记:
9F37
- 值:04
private ITlv.ITlvDataObjList parseTLV(byte[] src) {
try {
if (isBytesEmpty(src)) {
return null;
}
_tlvList = _tlv.createTlvDataObjectList();
int start = 0;
int end = start + src.length;
while (start < end) {
// tag has 1 byte (0xFF)
int tag = src[start++] & 0xFF;
//
if (tag == 0x00 || tag == 0xFF) {
continue;
}
// tag has more bytes?
if ((tag & 0x1F) == 0x1F) {
if (start >= src.length) {
break;
}
// tag has 2 bytes (0xFFFF)
tag = (tag << 8) | src[start++] & 0xFF;
// tag has 3 bytes (0xFFFFFF)
if ((tag & 0x80) != 0) {
if (start >= src.length) {
break;
}
tag = (tag << 8) | src[start++] & 0xFF;
}
// break when tag > 3 bytes
if ((tag & 0x80) != 0) {
continue;
}
}
// length 1 byte (0x7F)
int length = src[start++] & 0xFF;
// length has more bytes?
if (length >= 0x80) {
// break when length > 2 bytes
int count = length & 0x7F;
if (count > 3) {
continue;
}
// length 1 bytes (0x80-0xFF) or 2 bytes (0x100-0xFFFF)
length = 0;
for (int k = 0; k < count; k++) {
if (start >= src.length) {
break;
}
length = (length << 8) | src[start++] & 0xFF;
}
}
// values
byte[] value = new byte[length];
System.arraycopy(src, start, value, 0, length);
//
// create tlv object
ITlv.ITlvDataObj tlvObj = _tlv.createTlvDataObject();
tlvObj.setTag(tag);
tlvObj.setValue(value);
// save tlv object to list
_tlvList.addDataObj(tlvObj);
//
// next tag
while (tag > 0xFF) {
tag = tag >> 8;
}
// is constructed (has child)?
if ((tag & 0x20) != 0) {
continue;
}
start = start + value.length;
}
return _tlvList;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
如果不完全遵守 EMV 标准,您的代码可能会因以下常见原因而失败
- 标签可能会超过两个字节(从代码来看,你处理的标签不会超过两个字节,但是遇到的几率很小)
- 长度可能超过一个字节(尽管长度通常为一个字节)。
- 如果您尝试将标签与列表进行比较(如果不这样做,请忽略此),EMV 引入的任何新标签都将失败。
EMV 4.3 Book 3,BER-TLV 数据对象的附件 B 规则部分 B1、B2、B3 是正确的查找位置。如果你严格按照这个去做,以上的一切都可以避免。
标签 8C
不包含构造值。因此,您对 ((tag & 0x20) != 0)
的测试正确失败。相反,该 TLV 包含一个数据对象列表作为其值。当你查看值9F02069F03069F1A0295055F2A029A039C019F3704
时,你会发现这些不是完整的TLV对象,而只是标签+长度。
因此,您将不得不根据标签找出该TLV对象是否包含数据对象列表。然后您可以使用与解析完整 TLV 对象类似的方式解析该列表,只是您跳过了空值字段。
我的方法 parseTLV()
有时不能正确地降级为构造值(子 TLV)。我使用过滤器 ((tag & 0x20)! = 0)
来检测构造值,但有时它无法按预期工作。
例如
70178C159F02069F03069F1A0295055F2A029A039C019F3704
导致 parseTLV
的以下结果:
- 标记:
70
- 值:8C159F02069F03069F1A0295055F2A029A039C019F3704
- 标记:
8C
- 值:9F02069F03069F1A0295055F2A029A039C019F3704
不过,我希望得到
- 标记:
70
- 值:8C159F02069F03069F1A0295055F2A029A039C019F3704
- 标记:
8C
- 值:9F02069F03069F1A0295055F2A029A039C019F3704
- 标记:
9F02
- 值:06
- 标记:
9F03
- 值:06
- 标记:
9F1A
- 值:02
- 标记:
95
- 值:05
- 标记:
5F2A
- 值:02
- 标记:
9A
- 值:03
- 标记:
9C
- 值:01
- 标记:
9F37
- 值:04
- 标记:
private ITlv.ITlvDataObjList parseTLV(byte[] src) {
try {
if (isBytesEmpty(src)) {
return null;
}
_tlvList = _tlv.createTlvDataObjectList();
int start = 0;
int end = start + src.length;
while (start < end) {
// tag has 1 byte (0xFF)
int tag = src[start++] & 0xFF;
//
if (tag == 0x00 || tag == 0xFF) {
continue;
}
// tag has more bytes?
if ((tag & 0x1F) == 0x1F) {
if (start >= src.length) {
break;
}
// tag has 2 bytes (0xFFFF)
tag = (tag << 8) | src[start++] & 0xFF;
// tag has 3 bytes (0xFFFFFF)
if ((tag & 0x80) != 0) {
if (start >= src.length) {
break;
}
tag = (tag << 8) | src[start++] & 0xFF;
}
// break when tag > 3 bytes
if ((tag & 0x80) != 0) {
continue;
}
}
// length 1 byte (0x7F)
int length = src[start++] & 0xFF;
// length has more bytes?
if (length >= 0x80) {
// break when length > 2 bytes
int count = length & 0x7F;
if (count > 3) {
continue;
}
// length 1 bytes (0x80-0xFF) or 2 bytes (0x100-0xFFFF)
length = 0;
for (int k = 0; k < count; k++) {
if (start >= src.length) {
break;
}
length = (length << 8) | src[start++] & 0xFF;
}
}
// values
byte[] value = new byte[length];
System.arraycopy(src, start, value, 0, length);
//
// create tlv object
ITlv.ITlvDataObj tlvObj = _tlv.createTlvDataObject();
tlvObj.setTag(tag);
tlvObj.setValue(value);
// save tlv object to list
_tlvList.addDataObj(tlvObj);
//
// next tag
while (tag > 0xFF) {
tag = tag >> 8;
}
// is constructed (has child)?
if ((tag & 0x20) != 0) {
continue;
}
start = start + value.length;
}
return _tlvList;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
如果不完全遵守 EMV 标准,您的代码可能会因以下常见原因而失败
- 标签可能会超过两个字节(从代码来看,你处理的标签不会超过两个字节,但是遇到的几率很小)
- 长度可能超过一个字节(尽管长度通常为一个字节)。
- 如果您尝试将标签与列表进行比较(如果不这样做,请忽略此),EMV 引入的任何新标签都将失败。
EMV 4.3 Book 3,BER-TLV 数据对象的附件 B 规则部分 B1、B2、B3 是正确的查找位置。如果你严格按照这个去做,以上的一切都可以避免。
标签 8C
不包含构造值。因此,您对 ((tag & 0x20) != 0)
的测试正确失败。相反,该 TLV 包含一个数据对象列表作为其值。当你查看值9F02069F03069F1A0295055F2A029A039C019F3704
时,你会发现这些不是完整的TLV对象,而只是标签+长度。
因此,您将不得不根据标签找出该TLV对象是否包含数据对象列表。然后您可以使用与解析完整 TLV 对象类似的方式解析该列表,只是您跳过了空值字段。