Talk to your Credit Card part 7: find and print out the “Application Primary Account Number” (“PAN”) = card number

AndroidCrypto
5 min readApr 12, 2023

--

In step 6 we anticipated the “find part” by visual examination of the prettyPrint response and here I’m explaining how to do this programmatically.

We are trying to find tag 0x54 (PAN) and 0x5f24 (Expiration Date) in the read response with our BER-TLV parser and if the tag is not null the tag was found and, after removing any trailing “F”s printed out (same for Expiration Date).

... code for generating the read command
readRecordResponse = nfc.transceive(cmd);
byte[] readRecordResponseTag5a = null;
byte[] readRecordResponseTag5f24 = null;
if (readRecordResponse != null) {
try {
BerTlvs tlvsReadRecord = parser.parse(readRecordResponse);
BerTlv tag5a = tlvsReadRecord.find(new BerTag(0x5a));
if (tag5a != null) {
readRecordResponseTag5a = tag5a.getBytesValue();
writeToUiAppend("found tag 0x5a in the readRecordResponse length: " + readRecordResponseTag5a.length + " data: " + bytesToHexNpe(readRecordResponseTag5a));
}
BerTlv tag5f24 = tlvsReadRecord.find(new BerTag(0x5f, 0x24));
if (tag5f24 != null) {
readRecordResponseTag5f24 = tag5f24.getBytesValue();
writeToUiAppend("found tag 0x5f24 in the readRecordResponse length: " + readRecordResponseTag5f24.length + " data: " + bytesToHexNpe(readRecordResponseTag5f24));
}
if (readRecordResponseTag5a != null) {
String readRecordPanString = removeTrailingF(bytesToHexNpe(readRecordResponseTag5a));
String readRecordExpirationDateString = bytesToHexNpe(readRecordResponseTag5f24);
writeToUiAppend("PAN: " + readRecordPanString);
String expirationDateString = "Expiration date (" + (readRecordExpirationDateString.length() == 4 ? "YYMM): " : "YYMMDD): ") + readRecordExpirationDateString;
}
} catch (RuntimeException e) {
System.out.println("Runtime Exception: " + e.getMessage());
}
}

Of course you can search for Track 1 or Track 2 data as well but they are not available on every card I read so far.

Here is one record from my VisaCard used in previous steps:

readRecord  command length: 5 data: 00b2031400
readRecord response length: 236 data: 7081e79f4701039f4681b04eeaeb38be423b4bc781eceb83755a26476bb43f98492fe49df28e1380b2b97579d0abc96099031baeb7e4620dcc1af54b67c4f876206d506fa641a3a06f4a8bf8b8f0c3aa9edc61562dd511a5efd4f7fd2fbe50020ef5bcda441d735be43cbcd08e9abbbae156c53811fe86f15201c1e84a8a7d70d9be856269b81110e230182eedf46a00a3223800cd76f0f9259d11a7d2d50ee20a5caa1b09cc77f76bc6c8b2eeb5e93c58962fa1b7a004c7b28cc25a0842635401222700509f690701af211c3b38005f24032506309f0702ffc05f280202769f4a01829f6e04207000009000
------------------------------------
70 81 E7 -- Record Template (EMV Proprietary)
9F 47 01 -- ICC Public Key Exponent
03 (BINARY)
9F 46 81 B0 -- ICC Public Key Certificate
4E EA EB 38 BE 42 3B 4B C7 81 EC EB 83 75 5A 26
47 6B B4 3F 98 49 2F E4 9D F2 8E 13 80 B2 B9 75
79 D0 AB C9 60 99 03 1B AE B7 E4 62 0D CC 1A F5
4B 67 C4 F8 76 20 6D 50 6F A6 41 A3 A0 6F 4A 8B
F8 B8 F0 C3 AA 9E DC 61 56 2D D5 11 A5 EF D4 F7
FD 2F BE 50 02 0E F5 BC DA 44 1D 73 5B E4 3C BC
D0 8E 9A BB BA E1 56 C5 38 11 FE 86 F1 52 01 C1
E8 4A 8A 7D 70 D9 BE 85 62 69 B8 11 10 E2 30 18
2E ED F4 6A 00 A3 22 38 00 CD 76 F0 F9 25 9D 11
A7 D2 D5 0E E2 0A 5C AA 1B 09 CC 77 F7 6B C6 C8
B2 EE B5 E9 3C 58 96 2F A1 B7 A0 04 C7 B2 8C C2 (BINARY)
5A 08 -- Application Primary Account Number (PAN)
42 63 54 01 22 27 00 50 (NUMERIC)
9F 69 07 -- UDOL
01 AF 21 1C 3B 38 00 (BINARY)
5F 24 03 -- Application Expiration Date
25 06 30 (NUMERIC)
9F 07 02 -- Application Usage Control
FF C0 (BINARY)
5F 28 02 -- Issuer Country Code
02 76 (NUMERIC)
9F 4A 01 -- Static Data Authentication Tag List
82 (BINARY)
9F 6E 04 -- Visa Low-Value Payment (VLP) Issuer Authorisation Code
20 70 00 00 (BINARY)
90 00 -- Command successfully executed (OK)
------------------------------------

The same procedure as with the MasterCard is to find the tags 0x5a and 0x5f24 and printout the Credit Card number and the Expiration Date.

At this point I’m repeating my warning from the beginning:

Never ever send the data you got by this app from an active card over internet or email as the data may contain confidential data even if you can’t see them.

Here is a short example that simple “masking” of credit card numbers can be dangerous. The VisaCard responded this data when reading a file from the AFL list:

------------------------------------
70 81 E7 -- Record Template (EMV Proprietary)
9F 47 01 -- ICC Public Key Exponent
03 (BINARY)
9F 46 81 B0 -- ICC Public Key Certificate
4E EA EB 38 BE 42 3B 4B C7 81 EC EB 83 75 5A 26
47 6B B4 3F 98 49 2F E4 9D F2 8E 13 80 B2 B9 75
79 D0 AB C9 60 99 03 1B AE B7 E4 62 0D CC 1A F5
4B 67 C4 F8 76 20 6D 50 6F A6 41 A3 A0 6F 4A 8B
F8 B8 F0 C3 AA 9E DC 61 56 2D D5 11 A5 EF D4 F7
FD 2F BE 50 02 0E F5 BC DA 44 1D 73 5B E4 3C BC
D0 8E 9A BB BA E1 56 C5 38 11 FE 86 F1 52 01 C1
E8 4A 8A 7D 70 D9 BE 85 62 69 B8 11 10 E2 30 18
2E ED F4 6A 00 A3 22 38 00 CD 76 F0 F9 25 9D 11
A7 D2 D5 0E E2 0A 5C AA 1B 09 CC 77 F7 6B C6 C8
B2 EE B5 E9 3C 58 96 2F A1 B7 A0 04 C7 B2 8C C2 (BINARY)
5A 08 -- Application Primary Account Number (PAN)
42 63 54 01 22 27 00 50 (NUMERIC)
9F 69 07 -- UDOL
01 AF 21 1C 3B 38 00 (BINARY)
5F 24 03 -- Application Expiration Date
25 06 30 (NUMERIC)
9F 07 02 -- Application Usage Control
FF C0 (BINARY)
5F 28 02 -- Issuer Country Code
02 76 (NUMERIC)
9F 4A 01 -- Static Data Authentication Tag List
82 (BINARY)
9F 6E 04 -- Visa Low-Value Payment (VLP) Issuer Authorisation Code
20 70 00 00 (BINARY)
90 00 -- Command successfully executed (OK)
------------------------------------

One of the tags returned is tag 0x9f46 (ICC Public Key Certificate). The data is encrypted but we can decrypt the tag value with public available keys to this data (code not included in this app):

decryption of ICC Public Key success
Recovered Data Header Byte: 6a
Certificate Format: 4
Application Pan: 4263540122270050ffff
Certificate Expiration Date: 0625
Certificate Serial Number: 26e44b
...

Ups, the full credit card number and expiration date is shown in the decrypted key dump (compare it visually with the content of tags 0x5a and 0x5f24.

Now our journey ends as we get a last printout:

07 get PAN and Expiration date from tags 0x5a and 0x5f24
data for AID a0000000041010
PAN: 5375050000160110
Expiration date (YYMMDD): 240331

There are a lot of improvements possible regarding the code but I tried to make the code as much readable as possible and tried to avoid any nesting of commands.

We saw so many data in tags to evaluate so now you do have a stable basis for your work. Have fun with your coding.

I’m providing a compiled debug-app (see the debug-release folder of the GitHub repository below) or try this direct link: app-debug.apk.

Find the full code of the app in my GitHub repository TalkToYourCreditCardPart7: TalkToYourCreditCardPart7

--

--