package com.radio.www.m355.service;
import android.content.Context;
import android.content.Intent;
import android.nfc.cardemulation.HostApduService;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.radio.www.m355.ui.M355App;
import com.radio.www.m355.utils.Aes128EcbUtils;
import com.radio.www.m355.utils.HexDump;
import java.util.Arrays;
import javax.crypto.Cipher;
/**
* Created by Roy.lee
* On 2022/6/27
* Description:
*/
public class CardEmulationService extends HostApduService {
private static final String TAG = CardEmulationService.class.getSimpleName();
private static final String SEND = " : ==> ◇ ";
private static final String RECE = " : <== ◆ ";
private static Handler mHandler;
private static StringBuffer mSb;
private static final int INIT_CARD = 0;
private static final int READ_CARD = 1;
private static int MODE = READ_CARD;
private byte[] RANDOM_NUMBER = new byte[8];
private static final String str = "6f15840e315041592e5359532e4444463031a503880101";
public static Intent newHCEServiceIntent(Context context, Handler handler, StringBuffer mBuf){
Intent hceIntent = new Intent(context, CardEmulationService.class);
mHandler = handler;
mSb = mBuf;
return hceIntent;
}
@Override
public void onCreate() {
Log.i(TAG, "... CardEmulationService on create ...");
logAppend(TAG + " : ... CardEmulationService on create ...");
super.onCreate();
}
private void logAppend(String log){
if (mSb != null)
mSb.append(log + "\r\n");
if (mHandler != null)
mHandler.sendEmptyMessage(1);
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
Log.i(TAG, RECE + HexDump.toHexString(commandApdu));
logAppend(TAG + RECE + HexDump.toHexString(commandApdu));
String cmdApdu = HexDump.toHexString(HexDump.getSubArray(commandApdu,0,2));
Log.i(TAG, "cmdApdu : " + cmdApdu);
switch (cmdApdu){
case "00A4"://选择文件
if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_INIT_AID))){//
MODE = INIT_CARD;
logAppend(TAG + " : ... write card ...");
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_READ_AID))){//
MODE = READ_CARD;
logAppend(TAG + " : ... read card ...");
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_AID)) || Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_AIDS))){//
MODE = READ_CARD;
logAppend(TAG + " : ... read Aid ...");
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_FILE_DIRECTORY))){//
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_FILE_01))){//
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_FILE_02))){//
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_FILE_3F00))){ //3F00
logAppend(TAG + SEND + HexDump.toHexString(HexDump.concatenate(HexDump.hexStringToByteArray(str),ApduCommands.SW_9000)));
return HexDump.concatenate(HexDump.hexStringToByteArray(str),ApduCommands.SW_9000);
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.SELECT_FILE_DF99))){ //3F00
logAppend(TAG + SEND + HexDump.toHexString(HexDump.concatenate(HexDump.hexStringToByteArray(M355App.mmkv.decodeString("AppID")),ApduCommands.SW_9000)));
return HexDump.concatenate(HexDump.hexStringToByteArray(M355App.mmkv.decodeString("AppID")),ApduCommands.SW_9000);
}
break;
case "00B0"://读取数据
if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.READ_UID))){//
Long intUserCarcNmu = M355App.mmkv.decodeLong("UID",0);
// TODO 判断卡号
byte[] cardNum = new byte[5];
byte[] bytes = HexDump.longTo4Bytes(intUserCarcNmu);
System.arraycopy(bytes,0,cardNum,0,bytes.length);
logAppend(TAG + SEND + HexDump.toHexString(HexDump.concatenate(cardNum,ApduCommands.SW_9000)));
return HexDump.concatenate(cardNum,ApduCommands.SW_9000);
}
else if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.READ_CUID))){//
logAppend(TAG + SEND + HexDump.toHexString(HexDump.concatenate(HexDump.hexStringToByteArray(M355App.mmkv.decodeString("CUID","0000000000000000")),ApduCommands.SW_9000)));
return HexDump.concatenate(HexDump.hexStringToByteArray(M355App.mmkv.decodeString("CUID","0000000000000000")),ApduCommands.SW_9000);
}
break;
case "0084"://获取随机数
if (Arrays.equals(commandApdu, HexDump.hexStringToByteArray(ApduCommands.GET_CHALLENGE))){//
RANDOM_NUMBER = HexDump.getRand(8);
//logAppend(TAG + SEND + HexDump.toHexString(RANDOM_NUMBER));
logAppend(TAG + SEND + HexDump.toHexString(HexDump.concatenate(RANDOM_NUMBER,ApduCommands.SW_9000)));
return HexDump.concatenate(RANDOM_NUMBER,ApduCommands.SW_9000);
}
break;
case "0082"://外部认证
if (commandApdu.length == 13 && Arrays.equals(HexDump.getSubArray(commandApdu,0,5),HexDump.hexStringToByteArray(ApduCommands.EXTERNAL_AUTH))){
byte[] subArray = HexDump.getSubArray(commandApdu, 5, 8);
byte[] desDecrypt = new byte[8];
String external_auth_key = M355App.mmkv.decodeString("EXTERNAL_AUTH_KEY","");
if (external_auth_key == null || external_auth_key.equals("") ){
logAppend(TAG + SEND + "KEY 文件未找到");
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_6A82));
return ApduCommands.SW_6A82;
}
desDecrypt = Aes128EcbUtils.DESede(subArray,
HexDump.hexStringToByteArray(external_auth_key),
Cipher.DECRYPT_MODE);
//logAppend(TAG + RECE + HexDump.toHexString(desDecrypt));
if (Arrays.equals(RANDOM_NUMBER,desDecrypt)){
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}else {
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_63CF));
return ApduCommands.SW_63CF;
}
}
break;
case "0088"://内部认证
if (commandApdu.length == 13 && Arrays.equals(HexDump.getSubArray(commandApdu,0,5),HexDump.hexStringToByteArray(ApduCommands.INTERNAL_AUTH))){
byte[] randArray = HexDump.getSubArray(commandApdu, 5, 8);
logAppend(TAG + RECE + HexDump.toHexString(randArray));
byte[] desEncrypt = Aes128EcbUtils.Des(randArray,
HexDump.hexStringToByteArray(M355App.mmkv.decodeString("INTERNAL_AUTH_KEY")),
Cipher.ENCRYPT_MODE);
logAppend(TAG + SEND + HexDump.toHexString(HexDump.concatenate(desEncrypt,ApduCommands.SW_9000)));
return HexDump.concatenate(desEncrypt,ApduCommands.SW_9000);
}
break;
case "800E"://擦除当前目录文件
if (commandApdu.length == 5 && Arrays.equals(HexDump.getSubArray(commandApdu,0,5),HexDump.hexStringToByteArray(ApduCommands.ERASE_DF))){
// TODO 清除数据
clearCardData();
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
break;
case "80E0"://创建文件
if (commandApdu.length == 17 && Arrays.equals(commandApdu,HexDump.hexStringToByteArray(ApduCommands.CREATE_EC01_FILE))){
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (commandApdu.length == 12 && Arrays.equals(commandApdu,HexDump.hexStringToByteArray(ApduCommands.CREATE_SECRET_KEY_FILE))){
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (commandApdu.length == 12 && Arrays.equals(commandApdu,HexDump.hexStringToByteArray(ApduCommands.CREATE_FILE_01))){
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (commandApdu.length == 12 && Arrays.equals(commandApdu,HexDump.hexStringToByteArray(ApduCommands.CREATE_FILE_02))){
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
break;
case "80D4"://写KEY
if (commandApdu.length == 26 && Arrays.equals(HexDump.getSubArray(commandApdu,0,10),HexDump.hexStringToByteArray(ApduCommands.WRITE_EXTERNAL_AUTH_KEY))){
//TODO 保存外部认证秘钥
byte[] exAuthKeyBytes = HexDump.getSubArray(commandApdu,10,16);
M355App.mmkv.encode("EXTERNAL_AUTH_KEY",HexDump.toHexString(exAuthKeyBytes));
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
else if (commandApdu.length == 18 && Arrays.equals(HexDump.getSubArray(commandApdu,0,10),HexDump.hexStringToByteArray(ApduCommands.WRITE_INTERNAL_AUTH_KEY))){
//TODO 保存内部认证秘钥
byte[] inAuthKeyBytes = HexDump.getSubArray(commandApdu,10,8);
M355App.mmkv.encode("INTERNAL_AUTH_KEY",HexDump.toHexString(inAuthKeyBytes));
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
break;
case "00D6"://写uid
if (commandApdu.length == 9 && Arrays.equals(HexDump.getSubArray(commandApdu,0,5),HexDump.hexStringToByteArray(ApduCommands.WRITE_UID))){
byte[] uidBytes = HexDump.getSubArray(commandApdu,5,4);
//TODO 保存UID
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
M355App.mmkv.encode("UID",HexDump.toHexString(uidBytes));
return ApduCommands.SW_9000;
}
else if (commandApdu.length == 13 && Arrays.equals(HexDump.getSubArray(commandApdu,0,5),HexDump.hexStringToByteArray(ApduCommands.WRITE_CUID))){
//TODO 保存CUID
byte[] cuidBytes = HexDump.getSubArray(commandApdu,5,8);
M355App.mmkv.encode("CUID",HexDump.toHexString(cuidBytes));
if (mHandler != null)
mHandler.sendEmptyMessage(2);
logAppend(TAG + SEND + HexDump.toHexString(ApduCommands.SW_9000));
return ApduCommands.SW_9000;
}
break;
}
return ApduCommands.SW_6300;
}
private void clearCardData() {
M355App.mmkv.encode("UID","");
M355App.mmkv.encode("CUID","");
M355App.mmkv.encode("EXTERNAL_AUTH_KEY","");
M355App.mmkv.encode("INTERNAL_AUTH_KEY","");
}
@Override
public void onDeactivated(int reason) {
Log.i(TAG, "onDeactivated(). Reason: " + reason);
logAppend(TAG + "onDeactivated(). Reason: " + reason);
}
}