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

Posted

技术标签:

【中文标题】Girocard-Maestro 智能卡读卡器问题,读取持卡人姓名和 IBAN【英文标题】:Girocard-Maestro Smartcard reader issues, to read Card Holder Name and IBAN 【发布时间】:2016-03-25 03:25:31 【问题描述】:

按照on this card reader project 描述的例程,并使用this AID List,我能够读取 VISA 卡,而强制 AID 列表没有任何问题。现在我在阅读来自德国的 EC-Karten (Sparkasse Girocard) 时遇到了问题。

    当我尝试强制读取 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;
    

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

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

通过这个程序,我可以读取 BIC 和卡类型(“girocard”),以及 PDOL 等数据:

9F33029F35019F4001

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

    我已尝试通过 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");
    

正如我从包括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 值运行相同的程序来比较结果,看看会发生什么以便更好地理解。感谢您为我指明正确的方向。

【问题讨论】:

我不熟悉您要读取的芯片类型,但据我所知,67 00 表示命令长度错误。 这就是为什么我不是通过长度而是通过所有参数运行循环......并且在 APDUCommand apduGPO = new APDUCommand(0, 34, 193, 164,新字节[] 0x83,0,0);我认为这与处理选项中的不匹配有关,如***.com/questions/20807452/… 所示。无论如何,当我想获得最大长度时,为什么我必须将其设置为 95(阅读第一个问题)而不是 0? 我也有同样的问题。你现在拿到伊班了吗? 【参考方案1】:

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

原因是该项目中Transmit() 的实现存在错误。当你传递一个 Le 设置为 0 的 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 合规性,并且您收到的 PDOL 值为 9F33029F35019F4001,因此您可以尝试发出 GET PROCESSING OPTIONS 命令(类似于您当前尝试执行的操作)做)。由于卡片提供了PDOL,所以需要在GPO命令中填写PDOL相关数据对象的期望值。

PDOL 列出了以下要素:

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

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

0000 00 00

请注意,您的卡可能会在此处显示一些特定值。由于 PDOL 中的数据对象与它们在 EMV 中的定义不匹配(9F33(终端功能)的长度预计为 3,而 9F40(附加终端功能)的长度预计为 5),我可以' t 说出它们的实际含义/编码。

GPO 命令可能如下所示:

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

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

【讨论】:

对不起,我的错。 95长度的成功AID是A0000003591010028001,在原帖中编辑。感谢您的意见。

以上是关于Girocard-Maestro 智能卡读卡器问题,读取持卡人姓名和 IBAN的主要内容,如果未能解决你的问题,请参考以下文章

智能卡读卡器是不是支持所有智能卡型号?

发送 PPS 智能卡读卡器

智能卡读卡器的电源控制

智能卡读卡器插件(插入卡)事件

智能卡读卡器,无法读取某些卡

与 IC 读卡器和 SLE5528 智能卡配合使用