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>
|
||||
</activity>
|
||||
|
||||
<service android:name="com.vsmartcard.acardemulator.SimulatorService"
|
||||
<service android:name=".EmulatorService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_NFC_SERVICE">
|
||||
<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() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(SimulatorService.TAG)) {
|
||||
String capdu = intent.getStringExtra(SimulatorService.EXTRA_CAPDU);
|
||||
String rapdu = intent.getStringExtra(SimulatorService.EXTRA_RAPDU);
|
||||
String error = intent.getStringExtra(SimulatorService.EXTRA_ERROR);
|
||||
String deselect = intent.getStringExtra(SimulatorService.EXTRA_DESELECT);
|
||||
String install = intent.getStringExtra(SimulatorService.EXTRA_INSTALL);
|
||||
if (intent.getAction().equals(EmulatorService.TAG)) {
|
||||
String capdu = intent.getStringExtra(EmulatorService.EXTRA_CAPDU);
|
||||
String rapdu = intent.getStringExtra(EmulatorService.EXTRA_RAPDU);
|
||||
String error = intent.getStringExtra(EmulatorService.EXTRA_ERROR);
|
||||
String deselect = intent.getStringExtra(EmulatorService.EXTRA_DESELECT);
|
||||
String install = intent.getStringExtra(EmulatorService.EXTRA_INSTALL);
|
||||
if (install != null)
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_install) + ":" + install + "\n");
|
||||
if (capdu != null)
|
||||
@@ -102,7 +102,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (emulator != getString(R.string.vicc)) {
|
||||
Snackbar.make(view, "Scheduled re-installation of all applets...", Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show();
|
||||
SimulatorService.destroySimulator(getApplicationContext());
|
||||
EmulatorService.destroySimulator(getApplicationContext());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -116,7 +116,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this);
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(SimulatorService.TAG);
|
||||
intentFilter.addAction(EmulatorService.TAG);
|
||||
bManager.registerReceiver(bReceiver, intentFilter);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ package com.vsmartcard.acardemulator;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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.IntentResult;
|
||||
import com.vsmartcard.acardemulator.emulators.VICCEmulator;
|
||||
|
||||
/**
|
||||
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||
@@ -230,7 +230,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
h = uri.getHost();
|
||||
int _p = uri.getPort();
|
||||
if (_p < 0) {
|
||||
_p = SimulatorService.DEFAULT_PORT;
|
||||
_p = VICCEmulator.DEFAULT_PORT;
|
||||
}
|
||||
p = Integer.toString(_p);
|
||||
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