[.NET][C#]Parse ISO8583筆記(四)TLV

十幾年前傳統磁條卡片側錄盜刷事件層出不窮,當時高安全交易保護的晶片問世,ISO8583也依循著制定晶片規格的組織EMV增加了數十個晶片交易資訊欄位,按照ISO8583標準,晶片資訊在DE55,並以BER-TLV的規格(ISO8825)組成資料區塊,由於計算驗證ARQC/ARPC需要晶片交易欄位,我們要先Parse DE55。

 

 

ISO8583 晶片交易欄位是按照BER-TLV的標準制定,BER全名為Basic Encoding Rules,TLV全名則為Tag Length Value,簡單用下圖表示:

9F36代表EMV Tag: ATC 交易次數02表示Value長度0001則是Value,這邊要注意長度是實際長度而非16進位字串長度。

這邊我們注意到TLV Tag 第1個Byte 8bit內幾個特別的地方:

 

  • 前2個bit用來表示Tag class type:通用、應用、規範還是私用。
  • 第3個bit 表示資料屬於基本還是結構。
  • 後5個bit 若為11111,表示Tag name有2個Byte,例如我們剛剛舉的9F36 ATC的例子。

 

1. Parse EMV Tag程式碼:

 public class EMVTag
 {
     //Mask for Tag name 00011111
     public static byte DoubleByteMask = 0x1F;                 
     public byte[] Tag { get; set; }
     public string TagName
     {
         get
         {
             return Tag.BToHex();
         }
     }
     public int ValueLength { get; set; }
     public byte[] Value { get; set; }

     public int Parse(byte[] bData, ref int offset)
     {
         //(1)Tag Name
         Tag = GetTagName(bData, ref offset);
         //(2)Length
         ValueLength = GetValueLength(bData, ref offset);
         //(3)Value Data
         Value = new byte[ValueLength];
         Array.Copy(bData, offset, Value, 0, ValueLength);
         offset += ValueLength;
         //Loop End
         if (offset >= bData.Length)
         {
             offset = -1;
         }
         return offset;
     }

     public static byte[] GetTagName(byte[] bdata, ref int offset)
     {
         List<byte> TagName = new List<byte>();
         //Tag Name Byte(1)
         TagName.Add(bdata[offset]);
         //是否為兩個Byte的Tag name (與Mask進行&運算),且假設最多2個byte
         bool isNext = ((bdata[offset] & DoubleByteMask) == DoubleByteMask);
         offset++;
         while (isNext && offset < bdata.Length)
         {
             isNext = false;
             //Tag Name Byte(2)
             TagName.Add(bdata[offset]);
             offset++;
         }
         return TagName.ToArray();
     }
     public static int GetValueLength(byte[] data, ref int offset)
     {
         int len = (int)(data[offset]);
         offset++;
         return len;
     }

     public static List<EMVTag> ParseTags(byte[] data)
     {
         List<EMVTag> EMVTags = new List<EMVTag>();
         int offset = 0;
         while (offset >= 0)
         {
             EMVTag tag = new EMVTag();
             tag.Parse(data, ref offset);
             EMVTags.Add(tag);
         }
         return EMVTags;
     }

測試程式及晶片交易資料:

string sampleDE55 = @"
5F2A0201245F34010182021C008407A0000000031010
950580000000009A031102249B0268009C0100
9F02060000000000009F03060000000000009F0607A0000000031010
9F0802008C9F0902008C9F100706010A039000009F1A020124
9F2608423158936ED6C38F9F2701809F3303E0B0C8
9F34034103029F3501229F360200019F3704ACAC66E8
9F5800DF0100DF0200DF0400
";
//GetTLV(sampleDE55);
List<EMVTag> EmvTags = EMVTag.ParseTags(sampleDE55.Replace("\r\n", "").HexToByte());

foreach (var item in EmvTags)
{
    Console.WriteLine("{0}: {1}", item.TagName, item.Value.BToHex());
}

 

測試結果:

小結:

  • 國內區域主要清算中心為使國內銀行降低晶片化修改幅度,部分清算中心是將晶片交易資料藏於其他DE欄位中,與標準ISO8583作法不同。
  • 會不會遇到超過2個Byte的EMV Tag name?
  • Tag 說明可參考Emvlab

 

參考:

ISO8825

X.690

BER-TLV Encoding of EMV Tags

金融系統中BER-TLV的解析,更改、增加、刪除TAG的實現