I need to get the card number from a credit card using NFC and then convert it to a proper string.
Here is what I have so far:
private static readonly string MASTERCARD_AID = "A0000000041010";
// ISO-DEP command HEADER for selecting an AID.
// Format: [Class | Instruction | Parameter 1 | Parameter 2]
private static readonly string SELECT_APDU_HEADER = "00A40400";
// "OK" status word sent in response to SELECT AID command (0x9000)
private static readonly byte[] SELECT_OK_SW = { (byte)0x90, (byte)0x00 };
// Weak reference to prevent retain loop. mAccountCallback is responsible for exiting
// foreground mode before it becomes invalid (e.g. during onPause() or onStop()).
private WeakReference<AccountCallback> mAccountCallback;
public interface AccountCallback
{
void OnAccountRecieved(string account);
}
public LoyaltyCardReader(WeakReference<AccountCallback> accountCallback)
{
mAccountCallback = accountCallback;
}
/**
* Callback when a new tag is discovered by the system.
*
* <p>Communication with the card should take place here.
*
* @param tag Discovered tag
*/
public void OnTagDiscovered(Tag tag)
{
IsoDep isoDep = IsoDep.Get(tag);
if (isoDep != null)
{
try
{
// Connect to the remote NFC device
isoDep.Connect();
// Build SELECT AID command for our loyalty card service.
// This command tells the remote device which service we wish to communicate with.
byte[] command = BuildSelectApdu(MASTERCARD_AID);
// Send command to remote device
byte[] result = isoDep.Transceive(command);
// If AID is successfully selected, 0x9000 is returned as the status word (last 2
// bytes of the result) by convention. Everything before the status word is
// optional payload, which is used here to hold the account number.
int resultLength = result.Length; //should be 89
byte[] statusWord = { result[resultLength - 2], result[resultLength - 1] };
byte[] payload = new byte[resultLength - 2];
Array.Copy(result, payload, resultLength - 2);
bool arrayEquals = SELECT_OK_SW.Length == statusWord.Length;
for (int i = 0; i < SELECT_OK_SW.Length && i < statusWord.Length && arrayEquals; i++)
{
arrayEquals = (SELECT_OK_SW[i] == statusWord[i]);
if (!arrayEquals)
break;
}
if (arrayEquals)
{
//takes out cardname
//int lengthWanted = 58;
//byte[] newRes = new byte[resultLength - lengthWanted];
//byte[] cardname = new byte[newRes.Length - 15];
//Array.Copy(payload, newRes, resultLength - lengthWanted);
//Array.Copy(payload, 16, cardname, 0, cardname.Length);
// The remote NFC device will immediately respond with its stored account number
string accountNumber = Encoding.UTF8.GetString(payload);
//string accountNumber = ByteArrayToHexString(payload);
// Inform CardReaderFragment of received account number
AccountCallback accountCallback;
if (mAccountCallback.TryGetTarget(out accountCallback))
{
accountCallback.OnAccountRecieved(accountNumber);
}
}
}
catch (Exception e)
{
Console.WriteLine("Hmm: " + e);
throw e;
//Toast.MakeText(ctx, "hmmm: " + e, ToastLength.Short).Show();
}
}
}
/**
* Build APDU for SELECT AID command. This command indicates which service a reader is
* interested in communicating with. See ISO 7816-4.
*
* @param aid Application ID (AID) to select
* @return APDU for SELECT AID command
*/
public static byte[] BuildSelectApdu(string aid)
{
// Format: [CLASS | INSTRUCTION | PARAMETER 1 | PARAMETER 2 | LENGTH | DATA]
return HexStringToByteArray(SELECT_APDU_HEADER + (aid.Length / 2).ToString("X2") + aid);
}
/**
* Utility class to convert a byte array to a hexadecimal string.
*
* @param bytes Bytes to convert
* @return String, containing hexadecimal representation.
*/
public static string ByteArrayToHexString(byte[] bytes)
{
var hex = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
/**
* Utility class to convert a hexadecimal string to a byte string.
*
* <p>Behavior with input strings containing non-hexadecimal characters is undefined.
*
* @param s String containing hexadecimal characters to convert
* @return Byte array generated from input
*/
private static byte[] HexStringToByteArray(string s)
{
int len = s.Length;
if (len % 2 == 1)
{
throw new ArgumentException("Hex string must have even number of characters");
}
byte[] data = new byte[len / 2]; //Allocate 1 byte per 2 hex characters
for (int i = 0; i < len; i += 2)
{
ushort val, val2;
// Convert each chatacter into an unsigned integer (base-16)
try
{
val = (ushort)Convert.ToInt32(s[i].ToString() + "0", 16);
val2 = (ushort)Convert.ToInt32("0" + s[i + 1].ToString(), 16);
}
catch (Exception)
{
continue;
}
data[i / 2] = (byte)(val + val2);
}
return data;
}
I can extract the card type, but the rest of the result is gibberish, like boxed questionmarks and so on. I have tried to read all I could find on that subject, but I simply don't get it! :(
I am using Xamarin in Visual Studio 2015 Enterprise.
I can't remember where I got it but most of the code is taken from a great example where the author used another phone as emulator.