Проблемы со считывателем смарт-карт Girocard-Maestro, чтобы прочитать имя держателя карты и IBAN

Следуя процедурам, описанным в этом проекте чтения карт, и используя этот список 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 и тип карты («жирокарта»), а также такие данные, как 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, но отправка PDOL как

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

or

        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, возвращенными с карты, чтобы сравнить результаты и посмотреть, что произойдет, чтобы лучше понять. Спасибо, что указали мне правильное направление.


person Mario    schedule 18.12.2015    source источник
comment
Я не знаком с типом чипа, который вы пытаетесь прочитать, но, насколько мне известно, 67 00 указывает на неправильную длину команды.   -  person Pieter Witvoet    schedule 21.12.2015
comment
И именно поэтому я запустил цикл не по длине, а по всем параметрам... и получил только 9000 ответов на второй вопрос в APDUCommand apduGPO = new APDUCommand(0, 34, 193, 164, новый байт[] { 0x83 , 0 }, 0); Я думаю, что это скорее связано с несоответствием параметров обработки, как показано в stackoverflow.com/questions/20807452/. В любом случае, почему я должен установить его на 95 (читай первый вопрос), а не на 0, когда я хочу получить максимальную длину?   -  person Mario    schedule 22.12.2015
comment
У меня точно такая же проблема. У тебя уже есть ибан?   -  person Markus_DE_HH    schedule 07.02.2020


Ответы (1)


Почему мне нужно установить для Le значение 95 вместо 0 (чтобы получить максимальную длину)?

Причина в том, что реализация Transmit() в этом проекте содержит ошибки. Когда вы передаете объект APDUCommand, для которого Le установлено в 0, он неправильно обработает этот случай, как если бы 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

Однако на самом деле вам нужен был следующий APDU (с полем Le):

00 A4 0400 05 0102030405 00

Это можно исправить, различив два случая: Le отсутствует (указывает, что данные ответа не ожидаются, Ne = 0) и Le равен 0 (указывает, что ожидается до 256 байтов данных ответа, Ne = 256).

Какие команды идут дальше?

Поскольку вы не раскрыли AID выбранного приложения (или, что еще лучше, ответ SELECT), невозможно определить, какой протокол он может использовать. Похоже, пока что все команды отклоняются с ошибкой неправильной длины ( SW = 0x6700), что, похоже, связано с проблемой в первом вопросе.

Поскольку список AID, на который вы ссылались, указывает на некоторую форму соответствия EMV для приложения Girocard, и вы получили значение PDOL 9F33029F35019F4001, вы можете попробовать выполнить команду GET PROCESSING OPTIONS (аналогично тому, что вы сейчас пытаетесь сделать). Поскольку карта предоставила PDOL, вам необходимо заполнить объект данных, связанный с PDOL, в команде GPO ожидаемыми значениями.

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

person Michael Roland    schedule 22.12.2015
comment
Извините, это моя ошибка. Успешный AID длиной 95: A0000003591010028001, отредактировано в исходном сообщении. Спасибо за отзыв. - person Mario; 22.12.2015