added proper separation between JCardSim and VICC
Aditionally use the wording emulator within this project to not be confused with JCardSim's Simulator classes.
This commit is contained in:
@@ -37,7 +37,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name="com.vsmartcard.acardemulator.SimulatorService"
|
<service android:name=".EmulatorService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.BIND_NFC_SERVICE">
|
android:permission="android.permission.BIND_NFC_SERVICE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Frank Morgner
|
||||||
|
*
|
||||||
|
* This file is part of ACardEmulator.
|
||||||
|
*
|
||||||
|
* ACardEmulator is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* ACardEmulator is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* ACardEmulator. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.vsmartcard.acardemulator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.nfc.cardemulation.HostApduService;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.StrictMode;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import com.vsmartcard.acardemulator.emulators.Emulator;
|
||||||
|
import com.vsmartcard.acardemulator.emulators.JCEmulator;
|
||||||
|
import com.vsmartcard.acardemulator.emulators.VICCEmulator;
|
||||||
|
|
||||||
|
public class EmulatorService extends HostApduService {
|
||||||
|
|
||||||
|
public static final String TAG = "com.vsmartcard.acardemulator.EmulatorService";
|
||||||
|
public static final String EXTRA_CAPDU = "MSG_CAPDU";
|
||||||
|
public static final String EXTRA_RAPDU = "MSG_RAPDU";
|
||||||
|
public static final String EXTRA_ERROR = "MSG_ERROR";
|
||||||
|
public static final String EXTRA_DESELECT = "MSG_DESELECT";
|
||||||
|
public static final String EXTRA_INSTALL = "MSG_INSTALL";
|
||||||
|
|
||||||
|
private static Emulator emulator = null;
|
||||||
|
private static boolean do_destroy = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate () {
|
||||||
|
super.onCreate();
|
||||||
|
// force reloading applets if requested
|
||||||
|
do_destroy();
|
||||||
|
|
||||||
|
if (emulator == null) {
|
||||||
|
Log.d("", "Begin transaction");
|
||||||
|
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
String str_emulator = SP.getString("emulator", "");
|
||||||
|
if (str_emulator.equals(getString(R.string.vicc))) {
|
||||||
|
emulator = new VICCEmulator(
|
||||||
|
SP.getString("hostname", VICCEmulator.DEFAULT_HOSTNAME),
|
||||||
|
Integer.parseInt(SP.getString("port", Integer.toString(VICCEmulator.DEFAULT_PORT))));
|
||||||
|
} else {
|
||||||
|
emulator = new JCEmulator(this,
|
||||||
|
SP.getBoolean("activate_helloworld", false),
|
||||||
|
SP.getBoolean("activate_openpgp", false),
|
||||||
|
SP.getBoolean("activate_oath", false),
|
||||||
|
SP.getBoolean("activate_isoapplet", false),
|
||||||
|
SP.getBoolean("activate_gidsapplet", false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void destroySimulator(Context context) {
|
||||||
|
do_destroy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void do_destroy() {
|
||||||
|
if (do_destroy) {
|
||||||
|
emulator.destroy();
|
||||||
|
emulator = null;
|
||||||
|
do_destroy = false;
|
||||||
|
Intent i = new Intent(TAG);
|
||||||
|
i.putExtra(EXTRA_DESELECT, "Uninstalled all applets.");
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
do_destroy();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] processCommandApdu(byte[] capdu, Bundle extras) {
|
||||||
|
byte[] rapdu = emulator.process(capdu);
|
||||||
|
|
||||||
|
Intent i = new Intent(TAG);
|
||||||
|
i.putExtra(EXTRA_CAPDU, Util.byteArrayToHexString(capdu));
|
||||||
|
if (rapdu != null)
|
||||||
|
i.putExtra(EXTRA_RAPDU, Util.byteArrayToHexString(rapdu));
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
||||||
|
|
||||||
|
return rapdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeactivated(int reason) {
|
||||||
|
Intent i = new Intent(TAG);
|
||||||
|
|
||||||
|
Log.d("", "End transaction");
|
||||||
|
switch (reason) {
|
||||||
|
case DEACTIVATION_LINK_LOSS:
|
||||||
|
i.putExtra(EXTRA_DESELECT, "link lost");
|
||||||
|
emulator.deactivate();
|
||||||
|
break;
|
||||||
|
case DEACTIVATION_DESELECTED:
|
||||||
|
emulator.deactivate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,12 +48,12 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private BroadcastReceiver bReceiver = new BroadcastReceiver() {
|
private BroadcastReceiver bReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent.getAction().equals(SimulatorService.TAG)) {
|
if (intent.getAction().equals(EmulatorService.TAG)) {
|
||||||
String capdu = intent.getStringExtra(SimulatorService.EXTRA_CAPDU);
|
String capdu = intent.getStringExtra(EmulatorService.EXTRA_CAPDU);
|
||||||
String rapdu = intent.getStringExtra(SimulatorService.EXTRA_RAPDU);
|
String rapdu = intent.getStringExtra(EmulatorService.EXTRA_RAPDU);
|
||||||
String error = intent.getStringExtra(SimulatorService.EXTRA_ERROR);
|
String error = intent.getStringExtra(EmulatorService.EXTRA_ERROR);
|
||||||
String deselect = intent.getStringExtra(SimulatorService.EXTRA_DESELECT);
|
String deselect = intent.getStringExtra(EmulatorService.EXTRA_DESELECT);
|
||||||
String install = intent.getStringExtra(SimulatorService.EXTRA_INSTALL);
|
String install = intent.getStringExtra(EmulatorService.EXTRA_INSTALL);
|
||||||
if (install != null)
|
if (install != null)
|
||||||
textViewVPCDStatus.append(getResources().getString(R.string.status_install) + ":" + install + "\n");
|
textViewVPCDStatus.append(getResources().getString(R.string.status_install) + ":" + install + "\n");
|
||||||
if (capdu != null)
|
if (capdu != null)
|
||||||
@@ -102,7 +102,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (emulator != getString(R.string.vicc)) {
|
if (emulator != getString(R.string.vicc)) {
|
||||||
Snackbar.make(view, "Scheduled re-installation of all applets...", Snackbar.LENGTH_LONG)
|
Snackbar.make(view, "Scheduled re-installation of all applets...", Snackbar.LENGTH_LONG)
|
||||||
.setAction("Action", null).show();
|
.setAction("Action", null).show();
|
||||||
SimulatorService.destroySimulator(getApplicationContext());
|
EmulatorService.destroySimulator(getApplicationContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -116,7 +116,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
|
LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(SimulatorService.TAG);
|
intentFilter.addAction(EmulatorService.TAG);
|
||||||
bManager.registerReceiver(bReceiver, intentFilter);
|
bManager.registerReceiver(bReceiver, intentFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package com.vsmartcard.acardemulator;
|
|||||||
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.FragmentTransaction;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -41,6 +40,7 @@ import android.view.MenuItem;
|
|||||||
|
|
||||||
import com.google.zxing.integration.android.IntentIntegrator;
|
import com.google.zxing.integration.android.IntentIntegrator;
|
||||||
import com.google.zxing.integration.android.IntentResult;
|
import com.google.zxing.integration.android.IntentResult;
|
||||||
|
import com.vsmartcard.acardemulator.emulators.VICCEmulator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link PreferenceActivity} that presents a set of application settings. On
|
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||||
@@ -230,7 +230,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
h = uri.getHost();
|
h = uri.getHost();
|
||||||
int _p = uri.getPort();
|
int _p = uri.getPort();
|
||||||
if (_p < 0) {
|
if (_p < 0) {
|
||||||
_p = SimulatorService.DEFAULT_PORT;
|
_p = VICCEmulator.DEFAULT_PORT;
|
||||||
}
|
}
|
||||||
p = Integer.toString(_p);
|
p = Integer.toString(_p);
|
||||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|||||||
@@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Frank Morgner
|
|
||||||
*
|
|
||||||
* This file is part of ACardEmulator.
|
|
||||||
*
|
|
||||||
* ACardEmulator is free software: you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* ACardEmulator is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along with
|
|
||||||
* ACardEmulator. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.vsmartcard.acardemulator;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.nfc.cardemulation.HostApduService;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.StrictMode;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.licel.jcardsim.base.Simulator;
|
|
||||||
import com.licel.jcardsim.base.SimulatorRuntime;
|
|
||||||
import com.licel.jcardsim.samples.HelloWorldApplet;
|
|
||||||
import com.licel.jcardsim.utils.AIDUtil;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import openpgpcard.OpenPGPApplet;
|
|
||||||
import pkgYkneoOath.YkneoOath;
|
|
||||||
import net.pwendland.javacard.pki.isoapplet.IsoApplet;
|
|
||||||
import com.mysmartlogon.gidsApplet.GidsApplet;
|
|
||||||
|
|
||||||
public class SimulatorService extends HostApduService {
|
|
||||||
|
|
||||||
public static final int DEFAULT_PORT = 35963;
|
|
||||||
private static final String DEFAULT_HOSTNAME = "10.0.2.2";
|
|
||||||
|
|
||||||
private static Socket socket;
|
|
||||||
private static InputStream inputStream;
|
|
||||||
private static OutputStream outputStream;
|
|
||||||
|
|
||||||
public static final String TAG = "com.vsmartcard.acardemulator.SimulatorService";
|
|
||||||
public static final String EXTRA_CAPDU = "MSG_CAPDU";
|
|
||||||
public static final String EXTRA_RAPDU = "MSG_RAPDU";
|
|
||||||
public static final String EXTRA_ERROR = "MSG_ERROR";
|
|
||||||
public static final String EXTRA_DESELECT = "MSG_DESELECT";
|
|
||||||
public static final String EXTRA_INSTALL = "MSG_INSTALL";
|
|
||||||
|
|
||||||
private static Simulator simulator = null;
|
|
||||||
private static boolean do_destroy = false;
|
|
||||||
|
|
||||||
private boolean useVPCD() {
|
|
||||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
String emulator = SP.getString("emulator", "");
|
|
||||||
if (emulator.equals(getString(R.string.vicc)))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int port() {
|
|
||||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
return Integer.parseInt(SP.getString("port", Integer.toString(DEFAULT_PORT)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String hostname() {
|
|
||||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
return SP.getString("hostname", DEFAULT_HOSTNAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createSimulator() {
|
|
||||||
String aid, name, extra_install = "", extra_error = "";
|
|
||||||
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
simulator = new Simulator(new SimulatorRuntime());
|
|
||||||
|
|
||||||
if (SP.getBoolean("activate_helloworld", false)) {
|
|
||||||
name = getResources().getString(R.string.applet_helloworld);
|
|
||||||
aid = getResources().getString(R.string.aid_helloworld);
|
|
||||||
try {
|
|
||||||
simulator.installApplet(AIDUtil.create(aid), HelloWorldApplet.class);
|
|
||||||
extra_install += "\n" + name + " (AID: " + aid + ")";
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SP.getBoolean("activate_openpgp", false)) {
|
|
||||||
name = getResources().getString(R.string.applet_openpgp);
|
|
||||||
aid = getResources().getString(R.string.aid_openpgp);
|
|
||||||
try {
|
|
||||||
byte[] aid_bytes = Util.hexStringToByteArray(aid);
|
|
||||||
byte[] inst_params = new byte[aid.length() + 1];
|
|
||||||
inst_params[0] = (byte) aid_bytes.length;
|
|
||||||
System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length);
|
|
||||||
simulator.installApplet(AIDUtil.create(aid), OpenPGPApplet.class, inst_params, (short) 0, (byte) inst_params.length);
|
|
||||||
extra_install += "\n" + name + " (AID: " + aid + ")";
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SP.getBoolean("activate_oath", false)) {
|
|
||||||
name = getResources().getString(R.string.applet_oath);
|
|
||||||
aid = getResources().getString(R.string.aid_oath);
|
|
||||||
try {
|
|
||||||
byte[] aid_bytes = Util.hexStringToByteArray(aid);
|
|
||||||
byte[] inst_params = new byte[aid.length() + 1];
|
|
||||||
inst_params[0] = (byte) aid_bytes.length;
|
|
||||||
System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length);
|
|
||||||
simulator.installApplet(AIDUtil.create(aid), YkneoOath.class, inst_params, (short) 0, (byte) inst_params.length);
|
|
||||||
extra_install += "\n" + name + " (AID: " + aid + ")";
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SP.getBoolean("activate_isoapplet", false)) {
|
|
||||||
name = getResources().getString(R.string.applet_isoapplet);
|
|
||||||
aid = getResources().getString(R.string.aid_isoapplet);
|
|
||||||
try {
|
|
||||||
simulator.installApplet(AIDUtil.create(aid), IsoApplet.class);
|
|
||||||
extra_install += "\n" + name + " (AID: " + aid + ")";
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SP.getBoolean("activate_gidsapplet", false)) {
|
|
||||||
name = getResources().getString(R.string.applet_gidsapplet);
|
|
||||||
aid = getResources().getString(R.string.aid_gidsapplet);
|
|
||||||
try {
|
|
||||||
simulator.installApplet(AIDUtil.create(aid), GidsApplet.class);
|
|
||||||
extra_install += "\n" + name + " (AID: " + aid + ")";
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent i = new Intent(TAG);
|
|
||||||
if (!extra_error.isEmpty())
|
|
||||||
i.putExtra(EXTRA_ERROR, extra_error);
|
|
||||||
if (!extra_install.isEmpty())
|
|
||||||
i.putExtra(EXTRA_INSTALL, extra_install);
|
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate () {
|
|
||||||
super.onCreate();
|
|
||||||
// force reloading applets if requested
|
|
||||||
do_destroy();
|
|
||||||
|
|
||||||
Log.d("", "Begin transaction");
|
|
||||||
if (useVPCD()) {
|
|
||||||
if (socket == null) {
|
|
||||||
try {
|
|
||||||
if (android.os.Build.VERSION.SDK_INT > 9) {
|
|
||||||
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
|
|
||||||
StrictMode.setThreadPolicy(policy);
|
|
||||||
}
|
|
||||||
vpcdConnect();
|
|
||||||
sendPowerOn();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
vpcdDisconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (simulator == null)
|
|
||||||
createSimulator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void destroySimulator(Context context) {
|
|
||||||
do_destroy = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void do_destroy() {
|
|
||||||
if (do_destroy) {
|
|
||||||
simulator = null;
|
|
||||||
do_destroy = false;
|
|
||||||
Intent i = new Intent(TAG);
|
|
||||||
i.putExtra(EXTRA_DESELECT, "Uninstalled all applets.");
|
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
do_destroy();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] processCommandApdu(byte[] capdu, Bundle extras) {
|
|
||||||
Intent i = new Intent(TAG);
|
|
||||||
String extra_error = "";
|
|
||||||
byte[] rapdu = null;
|
|
||||||
|
|
||||||
i.putExtra(EXTRA_CAPDU, Util.byteArrayToHexString(capdu));
|
|
||||||
try {
|
|
||||||
if (useVPCD()) {
|
|
||||||
rapdu = transmit(capdu);
|
|
||||||
} else {
|
|
||||||
rapdu = simulator.transmitCommand(capdu);
|
|
||||||
}
|
|
||||||
if (rapdu != null)
|
|
||||||
i.putExtra(EXTRA_RAPDU, Util.byteArrayToHexString(rapdu));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
vpcdDisconnect();
|
|
||||||
extra_error += "Internal error";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!extra_error.isEmpty())
|
|
||||||
i.putExtra(EXTRA_ERROR, extra_error);
|
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
|
||||||
|
|
||||||
return rapdu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDeactivated(int reason) {
|
|
||||||
Intent i = new Intent(TAG);
|
|
||||||
|
|
||||||
Log.d("", "End transaction");
|
|
||||||
switch (reason) {
|
|
||||||
case DEACTIVATION_LINK_LOSS:
|
|
||||||
i.putExtra(EXTRA_DESELECT, "link lost");
|
|
||||||
if (useVPCD())
|
|
||||||
try {
|
|
||||||
sendPowerOff();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
vpcdDisconnect();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DEACTIVATION_DESELECTED:
|
|
||||||
i.putExtra(EXTRA_DESELECT, "deactivated");
|
|
||||||
if (useVPCD())
|
|
||||||
try {
|
|
||||||
sendReset();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
vpcdDisconnect();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (useVPCD()) {
|
|
||||||
//vpcdDisconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] receiveFromVPCD() throws IOException {
|
|
||||||
/* convert length from network byte order.
|
|
||||||
Note that Java always uses network byte order internally. */
|
|
||||||
int length1 = inputStream.read();
|
|
||||||
int length2 = inputStream.read();
|
|
||||||
if (length1 == -1 || length2 == -1) {
|
|
||||||
// EOF
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int length = (length1 << 8) + length2;
|
|
||||||
|
|
||||||
byte[] data = new byte[length];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
while (length > 0) {
|
|
||||||
int read = inputStream.read(data, offset, length);
|
|
||||||
if (read == -1) {
|
|
||||||
// EOF
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
offset += read;
|
|
||||||
length -= read;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] transmit(byte[] capdu) throws IOException {
|
|
||||||
sendToVPCD(capdu);
|
|
||||||
return receiveFromVPCD();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPowerOff() throws IOException {
|
|
||||||
Log.d("", "Power Off");
|
|
||||||
sendToVPCD(new byte[] {0x00});
|
|
||||||
}
|
|
||||||
private void sendPowerOn() throws IOException {
|
|
||||||
Log.d("", "Power On");
|
|
||||||
sendToVPCD(new byte[] {0x01});
|
|
||||||
}
|
|
||||||
private void sendReset() throws IOException {
|
|
||||||
Log.d("", "Reset");
|
|
||||||
sendToVPCD(new byte[] {0x02});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendToVPCD(byte[] data) throws IOException {
|
|
||||||
/* convert length to network byte order.
|
|
||||||
Note that Java always uses network byte order internally. */
|
|
||||||
byte[] length = new byte[2];
|
|
||||||
length[0] = (byte) (data.length >> 8);
|
|
||||||
length[1] = (byte) (data.length & 0xff);
|
|
||||||
outputStream.write(length);
|
|
||||||
|
|
||||||
outputStream.write(data, 0, data.length);
|
|
||||||
|
|
||||||
outputStream.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void vpcdConnect() throws IOException {
|
|
||||||
String hostname = hostname();
|
|
||||||
int port = port();
|
|
||||||
Log.d("", "Connecting to " + hostname + ":" + Integer.toString(port));
|
|
||||||
socket = new Socket(InetAddress.getByName(hostname), port);
|
|
||||||
outputStream = socket.getOutputStream();
|
|
||||||
inputStream = socket.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void vpcdDisconnect() {
|
|
||||||
Log.d("", "Disconnecting");
|
|
||||||
if (socket != null) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ignore
|
|
||||||
} finally {
|
|
||||||
socket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.vsmartcard.acardemulator.emulators;
|
||||||
|
|
||||||
|
public interface Emulator {
|
||||||
|
|
||||||
|
public void deactivate();
|
||||||
|
|
||||||
|
public void destroy();
|
||||||
|
|
||||||
|
public byte[] process(byte[] commandAPDU);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package com.vsmartcard.acardemulator.emulators;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import com.licel.jcardsim.base.Simulator;
|
||||||
|
import com.licel.jcardsim.base.SimulatorRuntime;
|
||||||
|
import com.licel.jcardsim.samples.HelloWorldApplet;
|
||||||
|
import com.licel.jcardsim.utils.AIDUtil;
|
||||||
|
import com.mysmartlogon.gidsApplet.GidsApplet;
|
||||||
|
import com.vsmartcard.acardemulator.EmulatorService;
|
||||||
|
import com.vsmartcard.acardemulator.R;
|
||||||
|
import com.vsmartcard.acardemulator.Util;
|
||||||
|
|
||||||
|
import net.pwendland.javacard.pki.isoapplet.IsoApplet;
|
||||||
|
|
||||||
|
import openpgpcard.OpenPGPApplet;
|
||||||
|
import pkgYkneoOath.YkneoOath;
|
||||||
|
|
||||||
|
public class JCEmulator implements Emulator {
|
||||||
|
|
||||||
|
private static Simulator simulator = null;
|
||||||
|
|
||||||
|
public JCEmulator(
|
||||||
|
Context context,
|
||||||
|
boolean activate_helloworld,
|
||||||
|
boolean activate_openpgp,
|
||||||
|
boolean activate_oath,
|
||||||
|
boolean activate_isoapplet,
|
||||||
|
boolean activate_gidsapplet) {
|
||||||
|
String aid, name, extra_install = "", extra_error = "";
|
||||||
|
simulator = new Simulator(new SimulatorRuntime());
|
||||||
|
|
||||||
|
if (activate_helloworld) {
|
||||||
|
name = context.getResources().getString(R.string.applet_helloworld);
|
||||||
|
aid = context.getResources().getString(R.string.aid_helloworld);
|
||||||
|
try {
|
||||||
|
simulator.installApplet(AIDUtil.create(aid), HelloWorldApplet.class);
|
||||||
|
extra_install += "\n" + name + " (AID: " + aid + ")";
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activate_openpgp) {
|
||||||
|
name = context.getResources().getString(R.string.applet_openpgp);
|
||||||
|
aid = context.getResources().getString(R.string.aid_openpgp);
|
||||||
|
try {
|
||||||
|
byte[] aid_bytes = Util.hexStringToByteArray(aid);
|
||||||
|
byte[] inst_params = new byte[aid.length() + 1];
|
||||||
|
inst_params[0] = (byte) aid_bytes.length;
|
||||||
|
System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length);
|
||||||
|
simulator.installApplet(AIDUtil.create(aid), OpenPGPApplet.class, inst_params, (short) 0, (byte) inst_params.length);
|
||||||
|
extra_install += "\n" + name + " (AID: " + aid + ")";
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activate_oath) {
|
||||||
|
name = context.getResources().getString(R.string.applet_oath);
|
||||||
|
aid = context.getResources().getString(R.string.aid_oath);
|
||||||
|
try {
|
||||||
|
byte[] aid_bytes = Util.hexStringToByteArray(aid);
|
||||||
|
byte[] inst_params = new byte[aid.length() + 1];
|
||||||
|
inst_params[0] = (byte) aid_bytes.length;
|
||||||
|
System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length);
|
||||||
|
simulator.installApplet(AIDUtil.create(aid), YkneoOath.class, inst_params, (short) 0, (byte) inst_params.length);
|
||||||
|
extra_install += "\n" + name + " (AID: " + aid + ")";
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activate_isoapplet) {
|
||||||
|
name = context.getResources().getString(R.string.applet_isoapplet);
|
||||||
|
aid = context.getResources().getString(R.string.aid_isoapplet);
|
||||||
|
try {
|
||||||
|
simulator.installApplet(AIDUtil.create(aid), IsoApplet.class);
|
||||||
|
extra_install += "\n" + name + " (AID: " + aid + ")";
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activate_gidsapplet) {
|
||||||
|
name = context.getResources().getString(R.string.applet_gidsapplet);
|
||||||
|
aid = context.getResources().getString(R.string.aid_gidsapplet);
|
||||||
|
try {
|
||||||
|
simulator.installApplet(AIDUtil.create(aid), GidsApplet.class);
|
||||||
|
extra_install += "\n" + name + " (AID: " + aid + ")";
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent i = new Intent(EmulatorService.TAG);
|
||||||
|
if (!extra_error.isEmpty())
|
||||||
|
i.putExtra(EmulatorService.EXTRA_ERROR, extra_error);
|
||||||
|
if (!extra_install.isEmpty())
|
||||||
|
i.putExtra(EmulatorService.EXTRA_INSTALL, extra_install);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
if (simulator != null) {
|
||||||
|
simulator.reset();
|
||||||
|
simulator = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] process(byte[] commandAPDU) {
|
||||||
|
return simulator.transmitCommand(commandAPDU);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deactivate() {
|
||||||
|
simulator.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package com.vsmartcard.acardemulator.emulators;
|
||||||
|
|
||||||
|
import android.os.StrictMode;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public class VICCEmulator implements Emulator {
|
||||||
|
public static final int DEFAULT_PORT = 35963;
|
||||||
|
public static final String DEFAULT_HOSTNAME = "10.0.2.2";
|
||||||
|
|
||||||
|
private static Socket socket;
|
||||||
|
private static InputStream inputStream;
|
||||||
|
private static OutputStream outputStream;
|
||||||
|
|
||||||
|
public VICCEmulator(String hostname, int port) {
|
||||||
|
if (socket == null) {
|
||||||
|
try {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT > 9) {
|
||||||
|
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||||
|
StrictMode.setThreadPolicy(policy);
|
||||||
|
}
|
||||||
|
vpcdConnect(hostname, port);
|
||||||
|
sendPowerOn();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
vpcdDisconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deactivate() {
|
||||||
|
try {
|
||||||
|
sendPowerOff();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
vpcdDisconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
vpcdDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] commandAPDU) {
|
||||||
|
try {
|
||||||
|
sendToVPCD(commandAPDU);
|
||||||
|
return receiveFromVPCD();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new byte[] {0x6D, 0x00};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPowerOff() throws IOException {
|
||||||
|
Log.d("", "Power Off");
|
||||||
|
sendToVPCD(new byte[] {0x00});
|
||||||
|
}
|
||||||
|
private void sendPowerOn() throws IOException {
|
||||||
|
Log.d("", "Power On");
|
||||||
|
sendToVPCD(new byte[] {0x01});
|
||||||
|
}
|
||||||
|
private void sendReset() throws IOException {
|
||||||
|
Log.d("", "Reset");
|
||||||
|
sendToVPCD(new byte[] {0x02});
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] receiveFromVPCD() throws IOException {
|
||||||
|
/* convert length from network byte order.
|
||||||
|
Note that Java always uses network byte order internally. */
|
||||||
|
int length1 = inputStream.read();
|
||||||
|
int length2 = inputStream.read();
|
||||||
|
if (length1 == -1 || length2 == -1) {
|
||||||
|
// EOF
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int length = (length1 << 8) + length2;
|
||||||
|
|
||||||
|
byte[] data = new byte[length];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
while (length > 0) {
|
||||||
|
int read = inputStream.read(data, offset, length);
|
||||||
|
if (read == -1) {
|
||||||
|
// EOF
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
offset += read;
|
||||||
|
length -= read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendToVPCD(byte[] data) throws IOException {
|
||||||
|
/* convert length to network byte order.
|
||||||
|
Note that Java always uses network byte order internally. */
|
||||||
|
byte[] length = new byte[2];
|
||||||
|
length[0] = (byte) (data.length >> 8);
|
||||||
|
length[1] = (byte) (data.length & 0xff);
|
||||||
|
outputStream.write(length);
|
||||||
|
|
||||||
|
outputStream.write(data, 0, data.length);
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void vpcdConnect(String hostname, int port) throws IOException {
|
||||||
|
Log.d("", "Connecting to " + hostname + ":" + Integer.toString(port));
|
||||||
|
socket = new Socket(InetAddress.getByName(hostname), port);
|
||||||
|
outputStream = socket.getOutputStream();
|
||||||
|
inputStream = socket.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void vpcdDisconnect() {
|
||||||
|
Log.d("", "Disconnecting");
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
} finally {
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user