Skip to content

Instantly share code, notes, and snippets.

@haseebpvt
Created August 11, 2020 10:54
Show Gist options
  • Select an option

  • Save haseebpvt/9bce85647433f1d42a99e91b5f452db1 to your computer and use it in GitHub Desktop.

Select an option

Save haseebpvt/9bce85647433f1d42a99e91b5f452db1 to your computer and use it in GitHub Desktop.
public class Helpers {
final protected static char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for (int j = 0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] hexToBytes (String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
import androidx.appcompat.app.AppCompatActivity;
import androidx.arch.core.util.Function;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.os.Bundle;
import android.nfc.tech.NfcA;
import android.nfc.Tag;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import java.util.Arrays;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private TextView counterTextView;
private int counter = 0;
private NfcAdapter nfcAdapter;
private String TAG = "Khalid_Test";
/////NFC COMMANDS
private final byte PWD_AUTH = (byte) 0x1B;
private final byte FAST_READ = (byte) 0x3A;
private final byte WRITE = (byte) 0xA2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
counterTextView = findViewById(R.id.counter_text_view);
initNfcAdapter();
}
private void initNfcAdapter() {
Log.d(TAG, "initNfcAdapter");
NfcManager nfcManager = (NfcManager) getSystemService(Context.NFC_SERVICE);
nfcAdapter = nfcManager.getDefaultAdapter();
}
@Override
protected void onResume() {
super.onResume();
enableNfcForegroundDispatch();
}
private void enableNfcForegroundDispatch() {
try {
Intent intent = new Intent(this, MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent nfcPendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, null, null);
} catch (IllegalStateException ex) {
Log.e(TAG, "Error enabling NFC foreground dispatch", ex);
}
}
@Override
public void onNewIntent(Intent intent) {
// onResume gets called after this to handle the intent
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent");
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Log.d(TAG, "Tag: " + Helpers.bytesToHex(detectedTag.getId()));
long Start = new Date().getTime();
try {
NfcA nfcA = NfcA.get(detectedTag);
nfcA.connect();
byte[] response;
////// Authentication
response = nfcA.transceive(new byte[]{
PWD_AUTH,
SampleAppKeys.PWD_NTAG21X[0], SampleAppKeys.PWD_NTAG21X[1], SampleAppKeys.PWD_NTAG21X[2], SampleAppKeys.PWD_NTAG21X[3]
});
Log.d(TAG, "Authentication result: " + Helpers.bytesToHex(response));
////// Fast Read
response = nfcA.transceive(new byte[]{
FAST_READ,
(byte) 0x04,// Start Page
(byte) 0x28 // End Page
// (byte) 0x2C, // All pages
});
Log.d(TAG, "Fast read result: " + Helpers.bytesToHex(response));
////// parse message
PrepaidMessage message = NfcReadWrite.parse_decoded_message(response);
if (message != null) {
Log.d(TAG, "customerID: " + message.customerID);
Log.d(TAG, "amount: " + message.amount);
Log.d(TAG, "PrepaidMessage: " + message.toString());
} else
Log.d(TAG, "Empty PrepaidMessage (returned null).");
////////////////STARTING OF CHANGES (APPLICATION LOGIC)
message.transactionid = String.valueOf(new Date().getTime());
Double OldBalance = Double.parseDouble(message.getAmount());
Double new_balance = OldBalance - 1;
message.amount = Double.toString(new_balance);
/////////////// STARTING OF SAVING NEW BALANCE TO THE CARD
byte[] newMessage = NfcReadWrite.encodePrepaidMessage(message);
if (newMessage == null) {
Toast.makeText(this, "MESSAGE NULL", Toast.LENGTH_SHORT).show();
return;
}
int size = newMessage.length;
if (size < 144) {
int index = 0;
int no_of_pages = size / 4;
if (0 != size % 4)
no_of_pages++;
byte[] temp = new byte[4];
Arrays.fill(temp, (byte) 0);
for (index = 0; index < no_of_pages; index++) {
if (index == (no_of_pages - 1)) {
if (0 == size % 4)
System.arraycopy(newMessage, (index) * 4, temp, 0, 4);
else
System.arraycopy(newMessage, (index) * 4, temp, 0,
(newMessage.length - (index * 4)));
} else {
System.arraycopy(newMessage, (index) * 4, temp, 0, 4);
}
try {
int blk = index + 4;
//Log.i("TAG", "Writing at :" + blk + " data : " + this.bytesToHex(temp));
nfcA.transceive(new byte[]{
WRITE,
(byte) blk, // page = 3
temp[0], temp[1], temp[2], temp[3]
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i("TAG", e.getMessage());
break;
}
Arrays.fill(temp, (byte) 0);
}
}
Log.d(TAG, "Message commited to the tag.. it was like: ");
PrepaidMessage savedMessage = NfcReadWrite.parse_decoded_message(newMessage);
Log.d(TAG, savedMessage.toString());
counter++;
String text = "Counter: " + counter + " | Amount: " + savedMessage.amount;
counterTextView.setText(text);
} catch (Exception ex) {
Log.d(TAG, "Ex: " + ex.getMessage());
}
long End = new Date().getTime();
Log.d(TAG, "Time: " + (End - Start) + " milliseconds");
}
}
import android.util.Log;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class NfcReadWrite {
///////////////////////////////////////////////////////////////////////////
// READ
///////////////////////////////////////////////////////////////////////////
public static PrepaidMessage decodePrepaidMessage(byte[] cardMessage) {
byte[] decryptedMessage;
PrepaidMessage prepaidMessage = new PrepaidMessage();
int length;
try {
byte[] encryptedMessage = new byte[cardMessage[0]];
System.arraycopy(cardMessage, 1, encryptedMessage, 0, encryptedMessage.length);
decryptedMessage = AESCrypto.decrypt(new String(SampleAppKeys.MESSAGE_ENCRP_KEY), encryptedMessage);
length = decryptedMessage.length;
if (length <= 144 && (byte) 0xEF == decryptedMessage[0]) {
int index = 0;
index += 1;
// Customer ID
int customerIdLength = decryptedMessage[index++];
byte[] customerIdBytes = new byte[customerIdLength];
System.arraycopy(decryptedMessage, index, customerIdBytes, 0, customerIdLength);
prepaidMessage.customerID = new String(customerIdBytes);
index += customerIdLength;
// Amount
int amountLength = decryptedMessage[index++];
byte[] amountBytes = new byte[amountLength];
System.arraycopy(decryptedMessage, index, amountBytes, 0, amountLength);
prepaidMessage.amount = new String(amountBytes);
index += amountLength;
// Transaction ID
int transactionIdLength = decryptedMessage[index++];
byte[] transactionIdBytes = new byte[transactionIdLength];
System.arraycopy(decryptedMessage, index, transactionIdBytes, 0, transactionIdLength);
prepaidMessage.transactionid = new String(transactionIdBytes);
index += transactionIdLength;
// Card validity
int cardValidityLength = decryptedMessage[index++];
byte[] cardValidityBytes = new byte[cardValidityLength];
System.arraycopy(decryptedMessage, index, cardValidityBytes, 0, cardValidityLength);
prepaidMessage.cardvalidity = new String(cardValidityBytes);
index += cardValidityLength;
// Category
int categoryLength = decryptedMessage[index++];
byte[] categoryBytes = new byte[categoryLength];
System.arraycopy(decryptedMessage, index, categoryBytes, 0, categoryLength);
prepaidMessage.category = new String(categoryBytes);
index += categoryLength;
// Points
int pointsLength = decryptedMessage[index++];
byte[] pointsBytes = new byte[pointsLength];
System.arraycopy(decryptedMessage, index, pointsBytes, 0, pointsLength);
prepaidMessage.points = new String(pointsBytes);
index += pointsLength;
// Total spend
int totalSpendLength = decryptedMessage[index++];
byte[] totalSpendBytes = new byte[totalSpendLength];
System.arraycopy(decryptedMessage, index, totalSpendBytes, 0, totalSpendLength);
prepaidMessage.totalSpend = new String(totalSpendBytes);
index += totalSpendLength;
// Is registered
int isRegisteredLength = decryptedMessage[index++];
byte[] isRegisteredBytes = new byte[isRegisteredLength];
System.arraycopy(decryptedMessage, index, isRegisteredBytes, 0, isRegisteredLength);
prepaidMessage.isRegistered = new String(isRegisteredBytes);
index += isRegisteredLength;
// Last updated timestamp
int lastUpdatedTimestampLength = decryptedMessage[index++];
byte[] lastUpdatedTimestampBytes = new byte[lastUpdatedTimestampLength];
System.arraycopy(decryptedMessage, index, lastUpdatedTimestampBytes, 0, lastUpdatedTimestampLength);
prepaidMessage.lastUsedTimestamp = new String(lastUpdatedTimestampBytes);
index += lastUpdatedTimestampLength;
// Is blacklisted
int isBlacklistedLength = decryptedMessage[index++];
byte[] isBlacklistedBytes = new byte[isBlacklistedLength];
System.arraycopy(decryptedMessage, index, isBlacklistedBytes, 0, isBlacklistedLength);
prepaidMessage.isBlacklisted = new String(isBlacklistedBytes);
index += isBlacklistedLength;
prepaidMessage.isValid = true;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return prepaidMessage;
}
///////////////////////////////////////////////////////////////////////////
// WRITE
///////////////////////////////////////////////////////////////////////////
public static byte[] encodePrepaidMessage(PrepaidMessage prepaidMessage) {
byte[] message = null;
byte[] final_message;
if (prepaidMessage.lastUsedTimestamp == null) {
prepaidMessage.lastUsedTimestamp = getFormattedDate();
}
/*
In Current Concept we are encoding Customer Name, Transaction ID, Amount and Card Validity.Here fields can be changed as per
requirement and business logic. We are using LV format_icon i.e. Length and Value for encoding the message. While decoding we will
use Length parameter to find the length of message as well as length of each section in the message with same section order.
We are also using one byte identifier for our message format_icon, it will be common in all message which will be used to identify
if encoded message in from our encoding application or not. This will help in unnecessary decoding entire message in case of wrong
encoded message or external encoded NFC chip*/
int length = 1 /*Identifier Byte */ + 1 /* Customer Name Length*/ + prepaidMessage.customerID.getBytes().length
+ 1 /*Amount Length*/ + prepaidMessage.getAmount().getBytes().length
+ 1 /*Transaction ID Length*/ + prepaidMessage.transactionid.getBytes().length
+ 1 /*Validity Length*/ + prepaidMessage.cardvalidity.getBytes().length
+ 1 /*Category Length*/ + prepaidMessage.category.getBytes().length
+ 1 /*Points Length*/ + prepaidMessage.getPoints().getBytes().length
+ 1 /*Total spend Length*/ + prepaidMessage.getTotalSpend().getBytes().length
+ 1 /*isRegistered Length*/ + prepaidMessage.isRegistered.getBytes().length
+ 1 /*Last used time Length*/ + prepaidMessage.lastUsedTimestamp.getBytes().length
+ 1 /*Is Blacklisted Length*/ + prepaidMessage.isBlacklisted.getBytes().length;
if (length < 144) {
message = new byte[length];
int index = 0;
message[index++] = (byte) 0xEF; /* Identifier Byte, can be changed or removed as per business logic*/
// Customer ID
int customerIdLength = prepaidMessage.customerID.getBytes().length;
message[index++] = (byte) customerIdLength;
System.arraycopy(prepaidMessage.customerID.getBytes(), 0, message, index, customerIdLength);
index += customerIdLength;
// Amount
int amountlen = prepaidMessage.getAmount().getBytes().length;
message[index++] = (byte) amountlen;
System.arraycopy(prepaidMessage.getAmount().getBytes(), 0, message, index, amountlen);
index += amountlen;
// Transaction ID
int trxidlen = prepaidMessage.transactionid.getBytes().length;
message[index++] = (byte) trxidlen;
System.arraycopy(prepaidMessage.transactionid.getBytes(), 0, message, index, trxidlen);
index += trxidlen;
// Card validity
int cardvaliditylen = prepaidMessage.cardvalidity.getBytes().length;
message[index++] = (byte) cardvaliditylen;
System.arraycopy(prepaidMessage.cardvalidity.getBytes(), 0, message, index,
cardvaliditylen);
index += cardvaliditylen;
// Category
int categoryLength = prepaidMessage.category.getBytes().length;
message[index++] = (byte) categoryLength;
System.arraycopy(prepaidMessage.category.getBytes(), 0, message, index, categoryLength);
index += categoryLength;
// Points
int pointsLen = prepaidMessage.getPoints().getBytes().length;
message[index++] = (byte) pointsLen;
System.arraycopy(prepaidMessage.getPoints().getBytes(), 0, message, index, pointsLen);
index += pointsLen;
// Total spend
int totalSpendLen = prepaidMessage.getTotalSpend().getBytes().length;
message[index++] = (byte) totalSpendLen;
System.arraycopy(prepaidMessage.getTotalSpend().getBytes(), 0, message, index, totalSpendLen);
index += totalSpendLen;
// Is registered
int isRegisteredLen = prepaidMessage.isRegistered.getBytes().length;
message[index++] = (byte) isRegisteredLen;
System.arraycopy(prepaidMessage.isRegistered.getBytes(), 0, message, index, isRegisteredLen);
index += isRegisteredLen;
// Last used timestamp
int lastUsedTimeLen = prepaidMessage.lastUsedTimestamp.getBytes().length;
message[index++] = (byte) lastUsedTimeLen;
System.arraycopy(prepaidMessage.lastUsedTimestamp.getBytes(), 0, message, index, lastUsedTimeLen);
index += lastUsedTimeLen;
// Is blacklisted
int isBlacklistedLength = prepaidMessage.isBlacklisted.getBytes().length;
message[index++] = (byte) isBlacklistedLength;
System.arraycopy(prepaidMessage.isBlacklisted.getBytes(), 0, message, index, isBlacklistedLength);
index += isBlacklistedLength;
} else {
// Toast.makeText(this, "message legth is large", Toast.LENGTH_SHORT).show();
}
/*Encrypt Message before encoding using AES Encryption method; this encryption provide second layer of security for message*/
try {
byte[] encrypted_message = AESCrypto.encrypt(new String(SampleAppKeys.MESSAGE_ENCRP_KEY), message);
final_message = new byte[encrypted_message.length + 1];
final_message[0] = (byte) encrypted_message.length;
System.arraycopy(encrypted_message, 0, final_message, 1, encrypted_message.length);
return final_message;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
///////////////////////////////////////////////////////////////////////////
// PARSE
///////////////////////////////////////////////////////////////////////////
public static PrepaidMessage parse_decoded_message(byte[] card_message) {
byte[] decryptedMessage;
PrepaidMessage prepaidMessage = new PrepaidMessage();
int length;
try {
byte[] encryptedMessage = new byte[card_message[0]];
System.arraycopy(card_message, 1, encryptedMessage, 0, encryptedMessage.length);
decryptedMessage = AESCrypto.decrypt(new String(SampleAppKeys.MESSAGE_ENCRP_KEY), encryptedMessage);
length = decryptedMessage.length;
if (length <= 144 && (byte) 0xEF == decryptedMessage[0]) {
int index = 0;
index += 1;
// Customer ID
int customerIdLength = decryptedMessage[index++];
byte[] customerIdBytes = new byte[customerIdLength];
System.arraycopy(decryptedMessage, index, customerIdBytes, 0, customerIdLength);
prepaidMessage.customerID = new String(customerIdBytes);
index += customerIdLength;
// Amount
int amountLength = decryptedMessage[index++];
byte[] amountBytes = new byte[amountLength];
System.arraycopy(decryptedMessage, index, amountBytes, 0, amountLength);
prepaidMessage.amount = new String(amountBytes);
index += amountLength;
// Transaction ID
int transactionIdLength = decryptedMessage[index++];
byte[] transactionIdBytes = new byte[transactionIdLength];
System.arraycopy(decryptedMessage, index, transactionIdBytes, 0, transactionIdLength);
prepaidMessage.transactionid = new String(transactionIdBytes);
index += transactionIdLength;
// Card validity
int cardValidityLength = decryptedMessage[index++];
byte[] cardValidityBytes = new byte[cardValidityLength];
System.arraycopy(decryptedMessage, index, cardValidityBytes, 0, cardValidityLength);
prepaidMessage.cardvalidity = new String(cardValidityBytes);
index += cardValidityLength;
// Category
int categoryLength = decryptedMessage[index++];
byte[] categoryBytes = new byte[categoryLength];
System.arraycopy(decryptedMessage, index, categoryBytes, 0, categoryLength);
prepaidMessage.category = new String(categoryBytes);
index += categoryLength;
// Points
int pointsLength = decryptedMessage[index++];
byte[] pointsBytes = new byte[pointsLength];
System.arraycopy(decryptedMessage, index, pointsBytes, 0, pointsLength);
prepaidMessage.points = new String(pointsBytes);
index += pointsLength;
// Total spend
int totalSpendLength = decryptedMessage[index++];
byte[] totalSpendBytes = new byte[totalSpendLength];
System.arraycopy(decryptedMessage, index, totalSpendBytes, 0, totalSpendLength);
prepaidMessage.totalSpend = new String(totalSpendBytes);
index += totalSpendLength;
// Is registered
int isRegisteredLength = decryptedMessage[index++];
byte[] isRegisteredBytes = new byte[isRegisteredLength];
System.arraycopy(decryptedMessage, index, isRegisteredBytes, 0, isRegisteredLength);
prepaidMessage.isRegistered = new String(isRegisteredBytes);
index += isRegisteredLength;
// Last updated timestamp
int lastUpdatedTimestampLength = decryptedMessage[index++];
byte[] lastUpdatedTimestampBytes = new byte[lastUpdatedTimestampLength];
System.arraycopy(decryptedMessage, index, lastUpdatedTimestampBytes, 0, lastUpdatedTimestampLength);
prepaidMessage.lastUsedTimestamp = new String(lastUpdatedTimestampBytes);
index += lastUpdatedTimestampLength;
// Is blacklisted
int isBlacklistedLength = decryptedMessage[index++];
byte[] isBlacklistedBytes = new byte[isBlacklistedLength];
System.arraycopy(decryptedMessage, index, isBlacklistedBytes, 0, isBlacklistedLength);
prepaidMessage.isBlacklisted = new String(isBlacklistedBytes);
index += isBlacklistedLength;
prepaidMessage.isValid = true;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return prepaidMessage;
}
// This method returns the current date and time formatted as the api requires
public static String getFormattedDate() {
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String date = simpleDateFormat.format(new Date());
return date;
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
public class PrepaidMessage {
public String customerID;
public String amount;
public String transactionid;
public String cardvalidity;
public String category;
public String points = "0";
public String totalSpend = "0";
public String isRegistered = "yes";
public String lastUsedTimestamp = getFormattedDate();
public String isBlacklisted = "no";
public boolean isValid = false;
public String getAmount() {
double value = Double.parseDouble(amount);
return String.format("%.2f", value);
// return amount;
}
public String getPoints() {
double value = 0;
try {
value = Double.parseDouble(points);
} catch (Exception e) {
}
return String.format("%.2f", value);
}
public String getTotalSpend() {
double value = Double.parseDouble(totalSpend);
return String.format("%.2f", value);
}
@Override
public String toString() {
return "PrepaidMessage{" +
"customerID='" + customerID + '\'' +
", amount='" + amount + '\'' +
", transactionid='" + transactionid + '\'' +
", cardvalidity='" + cardvalidity + '\'' +
", category='" + category + '\'' +
", points='" + points + '\'' +
", totalSpend='" + totalSpend + '\'' +
", isRegistered='" + isRegistered + '\'' +
", lastUsedTimestamp='" + lastUsedTimestamp + '\'' +
", isBlacklisted='" + isBlacklisted + '\'' +
'}';
}
// This method returns the current date and time formatted as the api requires
private String getFormattedDate() {
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String date = simpleDateFormat.format(new Date());
return date;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment