4

按照此读卡器项目中描述的例程,并使用此 AID 列表,我能够读取 VISA 卡,而强制使用 AID 列表没有任何问题。现在我在阅读来自德国的 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;
    

注:AID 选择成功读取为 A0000003591010028001

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

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

9F33029F35019F4001

然后我尝试按照这篇文章提高安全级别。但是在我的情况下,那些选择和读取的 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");
    

正如我从包括Openscdp 的 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,抛出异常 Fail Reading GPO as result is notsuccesful),所以我无法将 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 值运行相同的程序来比较结果,看看会发生什么以便更好地理解。感谢您为我指明正确的方向。

4

1 回答 1

1

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

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

...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

Note that your card might expect some specific values here. Since the data objects in the PDOL do not match their definition in EMV (9F33 (Terminal Capabilities) would be expected to have a length of 3 and 9F40 (Additional Terminal Capabilities) would be expected to have a length of 5), I can't tell their actual meaning/coding.

The GPO command could then look like:

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

Again, this will only work if you fix the issue with the Le field.

于 2015-12-22T10:26:37.613 回答