Girocard-Maestro智能卡阅读器问题,如何读取持卡人姓名和IBAN。

4

根据这个读卡器项目的描述,以及使用这个AID列表,我能够在不强制要求AID列表的情况下成功读取VISA卡。但我现在遇到了一个问题,无法读取德国的EC-Karten(Sparkasse Girocard)。

  1. When I try to force the AID List to be read, using

           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;
    

注意:选择成功读取的AID为A0000003591010028001

如果我不将APDU命令的长度参数明确设置为95而是使用标准的0(获取最大长度),它将不会响应90-00(成功),我通过迭代找到了这个值以确定可接受的长度。为什么?

通过此过程,我能够读取BIC和卡类型(“girocard”)以及诸如PDOL等数据:

9F33029F35019F4001

然后我尝试按照此帖子提高安全级别。但在我的情况下,选择和读取的那些APDU命令没有抛出90-00(而是6700)。

  1. I have tried to get the SFI records through

        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");
    

我从许多其他来源阅读到,包括Openscdp的启动应用程序过程,但将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);

无法进一步进行(再次出现错误6700,抛出异常,“无法成功读取GPO结果”)在apduGPOResponse上,所以我无法将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值相同的程序来比较结果,以便更好地理解发生了什么。感谢您指引我正确的方向。


我不熟悉您尝试读取的芯片类型,但据我所知,“67 00”表示命令长度错误。 - Pieter Witvoet
这就是为什么我没有通过Length而是运行了一个循环来处理所有的参数...在APDUCommand apduGPO = new APDUCommand(0, 34, 193, 164, new byte[] { 0x83, 0 }, 0);二进制数据的第二个问题上只得到了9000的响应。我认为这与Processing Options中的不匹配有关,如http://stackoverflow.com/questions/20807452/get-processing-options-always-6700wrong-lc-or-le所示。无论如何,当我想要获取最大长度时,为什么我必须将它设置为95(阅读第一个问题)而不是0? - Mario
我有同样的问题。你现在得到了IBAN吗? - Markus_DE_HH
1个回答

1

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

原因是该项目中Transmit()的实现存在缺陷。当您传递一个Le设置为0的APDUCommand对象时,它会错误地将该情况视为Le不存在,并因此不发送Le字段。请参见CardNative.cs的446行。因此,

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

以下APDU将被发送到卡片:

00 A4 0400 05 0102030405

然而,实际上您需要的是以下具有Le字段的APDU:
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),因此我无法确定它们的实际含义/编码。
GPO命令可能如下所示:
APDUCommand apduGPO = new APDUCommand(0x80, 0xa8, 0, 0, new byte[] { 0x83, 4, 0, 0, 0, 0 }, 0);

如果您修复了Le字段的问题,这将起作用。


抱歉,是我的错。AID 长度为 95 时成功了,编号为 A0000003591010028001,在原始帖子中已经进行了编辑。感谢您的反馈。 - Mario

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接