Girocard-Maestro智能卡读卡器问题,读取卡持有人姓名和IBAN

时间:2015-12-18 12:03:01

标签: c# credit-card apdu smartcard-reader emv

按照on this card reader project描述的例程,并使用this AID List,我能够在没有任何问题的情况下阅读VISA卡,从而强制执行A​​ID列表。现在我在阅读德国的EC-Karten(Sparkasse Girocard)时遇到了一个问题。

  1. 当我尝试使用

    强制读取AID列表时
           foreach (byte[] aid in aidList)
           {
    
               byte[] atrValue = this.cardUpdater.GetAttribute(SCARD_ATTR_VALUE.ATR_STRING);
               string strATR = ByteArrayToString(atrValue);
    
    
               APDUCommand apduSelectEMVApl = null;
               APDUResponse apdu2 = null;
    
               apduSelectEMVApl = new APDUCommand(0x00, 0xA4, 0x04, 0x00, aid, 95);
               apdu2 = this.cardUpdater.Transmit(apduSelectEMVApl);
    
              if (apdu2.SW1 == 0x90)
               {
                   //Label = ASCIIEncoding.ASCII.GetString(apdu2.Data, 15, apdu2.Data[14]);
                   //found it!
                   m_EMVAID = aid;
                   if (apdu2.Data[0] == 0x6f)  //fci template
                   {
                       ExtractData(ReadTagData(apdu2.Data, 0));
                   }
                   return true;
    
               }
    
           }
        return false;
    
  2.   

    注意:AID成功阅读以供选择是A0000003591010028001

    如果我没有将APDU命令的长度参数专门设置为95而不是标准0,如所有项目中所示(为了获得最大长度),它将不会响应90-00(成功)。我只是通过迭代发现了这个值,看看哪个长度是可以接受的。为什么呢?

    通过这个程序,我能够读取BIC和卡片类型(" girocard"),以及PDOL等数据:

      

    9F33029F35019F4001

    然后我尝试按照this post提高安全级别。但是那些选择和阅读的APDU命令不会在我的情况下扔掉90-00(而不是6700)。

    1. 我试图通过

      获取SFI记录
          APDUCommand apduGPO = new APDUCommand(0x80, 0xa8, 0, 0, new byte[] { 0x83, 0 }, 0);
          APDUResponse apdu1 = this.cardUpdater.Transmit(apduGPO);
          if (apdu1.SW1 != 0x90) throw new Exception("Read GPO Data fail");
          //two possible forms, 0x80 and 0x77
          if (apdu1.Data[0] == 0x80)
          {
              for (int i = 4; i < apdu1.Data.Length; i += 4)
              {
                  byte sfi = (byte)((apdu1.Data[i] >> 3) & 0xf);
                  byte lowRange = apdu1.Data[i + 1];
                  byte hiRange = apdu1.Data[i + 2];
                  byte[] records = new byte[hiRange - lowRange + 1];
                  for (int j = lowRange; j <= hiRange; j++)
                      records[j - lowRange] = (byte)j;
                  sfiRecords.Add(new SFIRecords(sfi, records));
              }
          }
          else if (apdu1.Data[0] == 0x77)
          {
              //look for the application file locator AFL
              int a, tag;
              for (a = 2; (tag = ReadTag(apdu1.Data, a)) != 0x94; a = SkipTag(apdu1.Data, a)) ;
              if (tag == 0x94)
              {
                  //found it
                  a++;
                  int len = apdu1.Data[a++];
                  for (int i = a; i < a + len; i += 4)
                  {
                      byte sfi = (byte)((apdu1.Data[i] >> 3) & 0xf);
                      byte lowRange = apdu1.Data[i + 1];
                      byte hiRange = apdu1.Data[i + 2];
                      byte[] records = new byte[hiRange - lowRange + 1];
                      for (int j = lowRange; j <= hiRange; j++)
                          records[j - lowRange] = (byte)j;
                      sfiRecords.Add(new SFIRecords(sfi, records));
                  }
              }
          }
          else
              throw new Exception("Unknown GPO template");
      
    2. 我从包括Openscdp's Initiate Application Process在内的许多其他来源中读到,但发送PDOL为

             APDUCommand apduGPO = new APDUCommand(0x80, 0xa8, 0, 0, pdol, 0);
             APDUResponse apdu1 = this.cardUpdater.Transmit(apduGPO);
      

              APDUCommand apduGPO = new APDUCommand(0x80, 0xa8, 0, 0, new byte[]{0x83, 0x00}, 0);
              APDUResponse apdu1 = this.cardUpdater.Transmit(apduGPO);
      

      在apduGPOResponse上没有再继续(错误6700,异常抛出失败读取GPO,因为结果不成功),所以我无法将SFI记录添加到列表中以进一步迭代Data [0 ]并查找要阅读的记录。没有90-00的反应。

      关于我缺少什么的任何想法?

      已更新

      我运行这段代码直接强制读取所有可能的值,而不使用GPO:

              APDUCommand apduReadAll = null;
              APDUResponse apdu1 = null;
               for (var sfi = 1; sfi <= 31; sfi++)
              {
                  for (var rec = 1; rec <= 16; rec++)
                  {
      
                      for (byte le = 0; le < 255; le++)
                      {
                          apduReadAll = new APDUCommand(0x00, 0xB2, (byte)rec, (byte)((sfi << 3) | 4), null, le);
                          apdu1 = this.cardUpdater.Transmit(apduReadAll);
      
                          if (apdu1.SW1 == 0x90)
                          {
                              Console.WriteLine("SFI " + sfi.ToString() + " record #" + rec);
                              if (apdu1.Data[0] == 0x70 || apdu1.Data[0] == 0x77)
                              {
                                  Console.WriteLine("Chalk one here " + sfi.ToString() + " record #" + rec + " len " + le);
                                  try
                                  {
                                      ExtractData(ReadTagData(apdu1.Data, 0));
                                  }
      
                                  catch
                                  {
      
                                  }
                                  //if (!String.IsNullOrEmpty(NumberString) && !String.IsNullOrEmpty(Name) &&
                                  //    !String.IsNullOrEmpty(ExpiryString) && !String.IsNullOrEmpty(CardType) &&
                                  //    !String.IsNullOrEmpty(Label))
                                  //    return;  //we have all info we need
                              }
                          }
                      }
                  }
              }
              foreach (TagData tag in Properties)
               {
                   Console.WriteLine(tag.Name + " " + tag.DataString);
                   strAllData += tag.Name + " " + tag.DataString + "\r\n";
               }
      

      在结果中我发现了一些我需要的信息(现在我可以直接指向我需要的数据来加快这个过程),以及其他一些有趣的信息:

      Application Label girocard
      Application Priority Indicator 02 
      Application Identifier (AID) - card A0 00 00 00 59 45 43 01 00 
      Application Label girocard
      Application Priority Indicator 04 
      Application Identifier (AID) - card A0 00 00 03 59 10 10 02 80 01 
      Application Label girocard
      Application Priority Indicator 04 
      Application Identifier (AID) - card A0 00 00 00 04 30 60 
      Application Label Maestro
      Application Priority Indicator 07 
      Application Identifier (AID) - card D2 76 00 00 25 45 50 02 00 
      Application Label GeldKarte
      Application Identifier (AID) - card A0 00 00 04 86 01 01 
      Application Label girocard
      Application Priority Indicator 05 
      

      我将使用卡中返回的AID值运行相同的程序来比较结果,看看会发生什么以便更好地理解。谢谢你指点我正确的方向。

1 个答案:

答案 0 :(得分:1)

为什么我需要将Le设置为95而不是0(以获得最大长度)?

原因是该项目中Transmit()的实施存在问题。当您传递一个已设置为Le的APDUCommand对象时,它将错误地处理该情况,就像Le不存在一样,因此不会发送Le字段。见CardNative.cs on line 446。因此,

...Transmit(new APDUCommand(0x00, 0xA4, 0x04, 0x00, new byte[] { 1, 2, 3, 4, 5 }, 0));

导致以下APDU被发送到卡:

00 A4 0400 05 0102030405

但是,您真正想要的是以下APDU(具有Le字段):

00 A4 0400 05 0102030405 00

这可以通过区分Le缺席的两种情况(表示没有预期的响应数据,Ne = 0)和Le为0(预期响应数据最多为256字节,Ne = 256)来解决。

接下来会有什么命令?

由于您没有透露所选应用程序的AID(或者更好的SELECT响应),因此无法确定它可能会说出哪个协议。到目前为止,所有命令似乎都得到了被错误的长度错误(SW = 0x6700)拒绝,这似乎与第一个问题中的问题有关。

由于您引用的AID列表指示Girocard应用程序的某种形式的EMV合规性并且您收到了9F33029F35019F4001的PDOL值,因此您可以尝试发出GET PROCESSING OPTIONS命令(类似于您当前的命令)试图做)。由于卡提供了PDOL,您需要使用期望值填充GPO命令中与PDOL相关的数据对象。

PDOL列出了以下要素:

  • 9F33(2字节)
  • 9F35(1字节)
  • 9F40(1字节)

因此,您可以尝试创建一个与零填充所有这些元素的PDOL相关数据对象:

0000 00 00

请注意,您的卡可能会在此处获得一些特定值。由于PDOL中的数据对象与EMV中的定义不匹配(9F33(终端能力)预计长度为3和9F40(附加终端能力)预计长度为5),我可以&# 39;告诉他们的实际意义/编码。

GPO命令可能如下所示:

APDUCommand apduGPO = new APDUCommand(0x80, 0xa8, 0, 0, new byte[] { 0x83, 4, 0, 0, 0, 0 }, 0);

同样,这只有在您解决Le字段的问题时才有效。