Following the routines described on this card reader project, and using this AID List , I was able to read a VISA Card without any problems forcing the AID List. Now I have a problem though with reading EC-Karten (Sparkasse Girocard) from Germany.
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;
Note: AID Successfuly read for selection is A0000003591010028001
If I don't set length parameter for the APDU Command specifically to 95 instead of the standard 0 as seen in all projects (to get the max length), it will not respond 90-00 (Success). I found this value just with an iteration to see which length was acceptable. Why?
With this procedure, I am able to read BIC and card type ("girocard"), as well as data such as PDOL:
9F33029F35019F4001
Then I try to raise the security level as per this post. But those APDU Commands to select and read don't throw 90-00 in my case (6700 instead).
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");
As I read from many other sources including Openscdp's Initiate Application Process, but sending the PDOL as
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);
doesn't go any further (error 6700 again, exception thrown Fail Reading GPO as result is not succesful) on the apduGPOResponse, so I cannot get to add the SFI Records to the list to further iterate with Data[0] and look for the records to read. No 90-00 response.
Any thoughts about what am I missing?
UPDATED
I ran this piece of code to force-read all possible values directly, without using 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";
}
In the results I found some of the info I needed (now I can directly point to the data I need to speed up the process), as well as some other interesting info:
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
I will run the same procedures with the mentioned AID values returned from the card to compare results and see what happens to better understand. Thanks for pointing me in the right direction.