Post on 11-Aug-2015
HCE tutorialA simple wallet
By Jensen
Outline
• Running environment • APDU format• Registration HCE service • Registration AID• APDU command list• Applet AID define • APDU command define• processCommandApdu• Card Reader side, APDU define • Card Reader side, send APDU• Seek-for-android
Running environment
• Card reader side • Java 8 (jre1.8.0.25)
• Android side• Android 4.4.2• Infocus M510
• IDE • Eclipse 4.4.1
• Code download• https://github.com/jensen0915/HCE_simple_wallet• https://github.com/jensen0915/HCE_simple_wallet_carder
Application protocol data unit (APDU) format
Source: http://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit
Registration HCE service
<service android:name="hce_demo.MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE" > <intent-filter> <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" /> </intent-filter>
<meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apduservice" /> </service>
Registration AID
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/servicedesc" android:requireDeviceUnlock="false" >
<aid-group android:category="other" android:description="@string/aiddescription" > <aid-filter android:name="F0394148148100" /> </aid-group>
</host-apdu-service>
APDU command list
• Applet AID • Add money• Sub money• Check balance
Applet AID define
private static final byte[] AID_SELECT_APDU = {(byte) 0x00, // CLA (class of command)(byte) 0xA4, // INS (instruction); A4 = select(byte) 0x04, // P1 (parameter 1) (0x04: select by name)(byte) 0x00, // P2 (parameter 2)(byte) 0x07, // LC (length of data) (byte) 0xF0, (byte) 0x39, (byte) 0x41, (byte) 0x48, (byte) 0x14, (byte) 0x81, (byte) 0x00,(byte) 0x00 // LE (max length of expected result, 0 implies 256)
};
APDU command define
private boolean selectAddMoneyApdu(byte[] apdu) { //(byte) 0x80, // CLA//(byte) 0x01, // INS//(byte) 0x00, // P1//(byte) 0x00, // P2return apdu.length >= 2 && apdu[0] == (byte) 0x80 && apdu[1] == (byte) 0x01 && apdu[2] == (byte) 0x00 && apdu[3] == (byte) 0x00;
}
private boolean selectDebitApdu(byte[] apdu) { //(byte) 0x80, // CLA//(byte) 0x02, // INS//(byte) 0x00, // P1//(byte) 0x00, // P2return apdu.length >= 2 && apdu[0] == (byte) 0x80 && apdu[1] == (byte) 0x02 && apdu[2] == (byte) 0x00 && apdu[3] == (byte) 0x00;
}
APDU command define (cont.)private boolean selectCheckBalanceApdu(byte[] apdu) {
//(byte) 0x80, // CLA//(byte) 0x03, // INS//(byte) 0x00, // P1//(byte) 0x00, // P2return apdu.length >= 2 && apdu[0] == (byte) 0x80 && apdu[1] == (byte) 0x03 && apdu[2] == (byte) 0x00 && apdu[3] == (byte) 0x00;
}
processCommandApdu
@Overridepublic byte[] processCommandApdu(byte[] apdu, Bundle extras) {
String inboundApduDescription;byte[] responseApdu;
if (Arrays.equals(AID_SELECT_APDU, apdu)) {inboundApduDescription = "Application selected";Log.i("HCEDEMO", inboundApduDescription);byte[] answer = new byte[2];answer[0] = (byte) 0x90;answer[1] = (byte) 0x00;responseApdu = answer;return responseApdu;
}
else if (selectAddMoneyApdu(apdu)) {Log.i("HCEDEMO", "ADD selected");int length = apdu[4];System.out.println("length = " + length);byte[] answer = new byte[3];
walletBalance = (byte)(walletBalance + apdu[5]);answer[0] = (byte) 0x90;answer[1] = (byte) 0x00;answer[2] = walletBalance;responseApdu = answer;return responseApdu;
}else if (selectDebitApdu(apdu)) {
Log.i("HCEDEMO", "Debit selected");int length = apdu[4];System.out.println("length = " + length);byte[] answer = new byte[3];
// balance can not be negativeif ( (byte)( (byte) walletBalance - apdu[5]) < (byte) 0 ) { answer[0] = (byte) 0x01;answer[1] = (byte) 0x02;responseApdu = answer;return responseApdu;
}
else if (selectCheckBalanceApdu(apdu)) {
Log.i("HCEDEMO", "check balance selected");byte[] answer = new byte[3];answer[0] = (byte) 0x90;answer[1] = (byte) 0x00;answer[2] = walletBalance;responseApdu = answer;return responseApdu;
}
else {Log.i("HCEDEMO", "Unknown command");byte[] answer = new byte[2];answer[0] = (byte) 0x6F;answer[1] = (byte) 0x00;responseApdu = answer;return responseApdu;
}}
Card Reader side, APDU define
//walletpublic static byte[] SelectAID = new byte[]{(byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x07,(byte) 0xF0, (byte) 0x39, (byte) 0x41, (byte) 0x48, (byte) 0x14, (byte) 0x81, (byte) 0x00, (byte) 0x00};
public static byte[] addMoney = new byte[]{(byte) 0x80, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x64};
public static byte[] subMoney = new byte[]{(byte) 0x80, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x34};
public static byte[] checkBalance = new byte[]{(byte) 0x80, (byte) 0x03, (byte) 0x00, (byte) 0x00};
Card Reader side, send APDU
public static void main(String[] args) throws UnsupportedEncodingException {TerminalFactory terminalFactory = TerminalFactory.getDefault();try {
for (CardTerminal terminal : terminalFactory.terminals().list()) {System.out.println(terminal.getName());try {
Card card = terminal.connect("*");CardChannel channel = card.getBasicChannel();
System.out.println("SelectAID ");CommandAPDU command = new CommandAPDU(SelectAID);ResponseAPDU response = channel.transmit(command);byte recv[] = response.getBytes();for (int i = 0; i < recv.length; i++) {System.out.print(String.format("%02X", recv[i]));}System.out.println("");
}
Demo
• Expectation output SelectAID 9000addMoney 900028subMoney 010200check balance 900028
Seek-for-android
https://code.google.com/p/seek-for-android/
Install Open Mobile API
• The Eclipse development environment needs to be configured in order to use the official SDK from Google as described in the Google documentation. In addition, the SDK needs to have access to the Open Mobile API addon package.
https://code.google.com/p/seek-for-android/wiki/UsingSmartCardAPI
AndroidManifest.xml
• <application• Add
<uses-library android:name="org.simalliance.openmobileapi" android:required="true" />
• <manifest• Add
<uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD" />
Register SEService
• Abstract function
Binding SEService
• protected void onCreate(Bundle savedInstanceState) {
Connection to SIM (secure element)
Send APDU
Face a problem
• Execution result • Reader openSession: service session is null.
• Unfortunately, we guess current open mobile API that doesn’t support HCE.