added remote smart card reader app
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package com.vsmartcard.remotesmartcardreader.app;
|
||||
|
||||
public class Hex {
|
||||
|
||||
private static final char[] BYTE2HEX=(
|
||||
"000102030405060708090A0B0C0D0E0F"+
|
||||
"101112131415161718191A1B1C1D1E1F"+
|
||||
"202122232425262728292A2B2C2D2E2F"+
|
||||
"303132333435363738393A3B3C3D3E3F"+
|
||||
"404142434445464748494A4B4C4D4E4F"+
|
||||
"505152535455565758595A5B5C5D5E5F"+
|
||||
"606162636465666768696A6B6C6D6E6F"+
|
||||
"707172737475767778797A7B7C7D7E7F"+
|
||||
"808182838485868788898A8B8C8D8E8F"+
|
||||
"909192939495969798999A9B9C9D9E9F"+
|
||||
"A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"+
|
||||
"B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"+
|
||||
"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
|
||||
"D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"+
|
||||
"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"+
|
||||
"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toCharArray();
|
||||
|
||||
public static String getHexString(byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
return "";
|
||||
}
|
||||
final int len=bytes.length;
|
||||
final char[] chars=new char[len<<1];
|
||||
int hexIndex;
|
||||
int idx=0;
|
||||
int ofs=0;
|
||||
while (ofs<len) {
|
||||
hexIndex=(bytes[ofs++] & 0xFF)<<1;
|
||||
chars[idx++]=BYTE2HEX[hexIndex++];
|
||||
chars[idx++]=BYTE2HEX[hexIndex];
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Layout;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.vsmartcard.remotesmartcardreader.app.screaders.DummyReader;
|
||||
import com.vsmartcard.remotesmartcardreader.app.screaders.NFCReader;
|
||||
import com.vsmartcard.remotesmartcardreader.app.screaders.SCReader;
|
||||
import android.text.ClipboardManager;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
class VPCDHandler extends Handler {
|
||||
VPCDHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case MessageSender.MESSAGE_CONNECTED:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_connected_to)+" "+message.obj+"\n");
|
||||
spinner.setVisibility(View.INVISIBLE);
|
||||
break;
|
||||
case MessageSender.MESSAGE_DISCONNECTED:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_disconnected)+"\n");
|
||||
testing = false;
|
||||
updateLabels();
|
||||
break;
|
||||
case MessageSender.MESSAGE_ATR:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_atr)+": "+message.obj+"\n");
|
||||
break;
|
||||
case MessageSender.MESSAGE_ON:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_on)+"\n");
|
||||
break;
|
||||
case MessageSender.MESSAGE_OFF:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_off)+"\n");
|
||||
break;
|
||||
case MessageSender.MESSAGE_RESET:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_reset)+"\n");
|
||||
break;
|
||||
case MessageSender.MESSAGE_CAPDU:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_capdu)+": "+message.obj+"\n");
|
||||
break;
|
||||
case MessageSender.MESSAGE_RAPDU:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_rapdu)+": "+message.obj+"\n");
|
||||
break;
|
||||
case MessageSender.MESSAGE_ERROR:
|
||||
if (testing) {
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_error)+": "+message.obj+"\n");
|
||||
testing = false;
|
||||
vpcdDisconnect();
|
||||
updateLabels();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_unknown)+" "+Integer.toString(message.what)+"\n");
|
||||
break;
|
||||
}
|
||||
Layout layout = textViewVPCDStatus.getLayout();
|
||||
if (layout != null) {
|
||||
int scrollAmount = layout.getLineTop(textViewVPCDStatus.getLineCount()) - textViewVPCDStatus.getHeight();
|
||||
if (scrollAmount > 0)
|
||||
textViewVPCDStatus.scrollTo(0, scrollAmount);
|
||||
else
|
||||
textViewVPCDStatus.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EditText editTextVPCDHost;
|
||||
private EditText editTextVPCDPort;
|
||||
private TextView textViewVPCDStatus;
|
||||
private TextView textViewInfo;
|
||||
private Button button;
|
||||
private ProgressBar spinner;
|
||||
private VPCDHandler handler;
|
||||
private VPCDWorker vpcdTest;
|
||||
private boolean testing;
|
||||
private String hostname;
|
||||
private String port;
|
||||
private String savedLog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
textViewInfo = (TextView) findViewById(R.id.textViewInfo);
|
||||
editTextVPCDHost = (EditText) findViewById(R.id.editTextHostname);
|
||||
editTextVPCDPort = (EditText) findViewById(R.id.editTextPort);
|
||||
textViewVPCDStatus = (TextView) findViewById(R.id.textViewLog);
|
||||
textViewVPCDStatus.setMovementMethod(new ScrollingMovementMethod());
|
||||
spinner = (ProgressBar) findViewById(R.id.progressBar);
|
||||
button = (Button) findViewById(R.id.buttonDisConnect);
|
||||
|
||||
EditText.OnEditorActionListener vpcdWatcher = new EditText.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
|
||||
if (i == EditorInfo.IME_ACTION_DONE) {
|
||||
editTextOnEditorDone();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
editTextVPCDHost.setOnEditorActionListener(vpcdWatcher);
|
||||
editTextVPCDPort.setOnEditorActionListener(vpcdWatcher);
|
||||
|
||||
handler = new VPCDHandler(Looper.getMainLooper());
|
||||
|
||||
PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
|
||||
|
||||
loadSettings();
|
||||
updateLabels();
|
||||
}
|
||||
|
||||
private static String saved_status_key = "textViewVPCDStatus";
|
||||
@Override
|
||||
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
textViewVPCDStatus.setText(savedInstanceState.getCharSequence(saved_status_key));
|
||||
}
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putCharSequence(saved_status_key, textViewVPCDStatus.getText());
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
testing = false;
|
||||
vpcdDisconnect();
|
||||
}
|
||||
|
||||
|
||||
public void buttonOnClickDisConnect(View view) {
|
||||
if (testing) {
|
||||
testing = false;
|
||||
vpcdDisconnect();
|
||||
} else {
|
||||
saveSettings();
|
||||
testing = true;
|
||||
spinner.setVisibility(View.VISIBLE);
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_dummy_start)+"\n");
|
||||
vpcdConnect(new DummyReader());
|
||||
}
|
||||
updateLabels();
|
||||
}
|
||||
|
||||
private void editTextOnEditorDone() {
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_dummy_start)+"\n");
|
||||
forceConnect(new DummyReader());
|
||||
}
|
||||
|
||||
private void vpcdConnect(SCReader scReader) {
|
||||
vpcdTest = new VPCDWorker(hostname, Integer.parseInt(port), scReader, handler);
|
||||
new Thread(vpcdTest).start();
|
||||
}
|
||||
|
||||
private void vpcdDisconnect() {
|
||||
if (vpcdTest != null) {
|
||||
vpcdTest.close();
|
||||
vpcdTest = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLabels() {
|
||||
if (testing) {
|
||||
button.setText(getResources().getString(R.string.button_stop_testing));
|
||||
} else {
|
||||
button.setText(getResources().getString(R.string.button_test_configuration));
|
||||
spinner.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSettings() {
|
||||
Settings settings = new Settings(this);
|
||||
hostname = settings.getVPCDHost();
|
||||
port = settings.getVPCDPort();
|
||||
editTextVPCDHost.setText(hostname);
|
||||
editTextVPCDPort.setText(port);
|
||||
}
|
||||
|
||||
private void saveSettings() {
|
||||
Editable textHostname = editTextVPCDHost.getText();
|
||||
Editable textPort = editTextVPCDPort.getText();
|
||||
if (textHostname != null) {
|
||||
hostname = textHostname.toString();
|
||||
}
|
||||
if (textPort != null) {
|
||||
port = textPort.toString();
|
||||
}
|
||||
Settings settings = new Settings(this);
|
||||
settings.setVPCDSettings(hostname, port);
|
||||
}
|
||||
|
||||
private void forceConnect(SCReader scReader) {
|
||||
if (testing) {
|
||||
testing = false;
|
||||
vpcdDisconnect();
|
||||
}
|
||||
saveSettings();
|
||||
testing = true;
|
||||
spinner.setVisibility(View.VISIBLE);
|
||||
vpcdConnect(scReader);
|
||||
updateLabels();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// Check to see that the Activity started due to a discovered tag
|
||||
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(getIntent().getAction())) {
|
||||
NFCReader nfcReader = NFCReader.processIntent(getIntent());
|
||||
if (nfcReader != null) {
|
||||
textViewVPCDStatus.append(getResources().getString(R.string.status_tag_discovered)+"\n");
|
||||
forceConnect(nfcReader);
|
||||
} else {
|
||||
super.onNewIntent(getIntent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
// onResume gets called after this to handle the intent
|
||||
setIntent(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public class MessageSender {
|
||||
public static final int MESSAGE_CONNECTED = 0;
|
||||
public static final int MESSAGE_DISCONNECTED = 1;
|
||||
public static final int MESSAGE_ERROR = 2;
|
||||
public static final int MESSAGE_ATR = 3;
|
||||
public static final int MESSAGE_ON = 4;
|
||||
public static final int MESSAGE_OFF = 5;
|
||||
public static final int MESSAGE_RESET = 6;
|
||||
public static final int MESSAGE_CAPDU = 7;
|
||||
public static final int MESSAGE_RAPDU = 8;
|
||||
private Handler handler;
|
||||
|
||||
public MessageSender(Handler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
private void sendMessage(int messageCode, Object object) {
|
||||
Message message = handler.obtainMessage(messageCode, object);
|
||||
handler.sendMessage(message);
|
||||
}
|
||||
|
||||
public void sendConnected(String details) {
|
||||
sendMessage(MESSAGE_CONNECTED, details);
|
||||
}
|
||||
|
||||
public void sendATR(String details) {
|
||||
sendMessage(MESSAGE_ATR, details);
|
||||
}
|
||||
|
||||
public void sendCAPDU(String details) {
|
||||
sendMessage(MESSAGE_CAPDU, details);
|
||||
}
|
||||
|
||||
public void sendDisconnected(String details) {
|
||||
sendMessage(MESSAGE_DISCONNECTED, details);
|
||||
}
|
||||
|
||||
public void sendError(String details) {
|
||||
sendMessage(MESSAGE_ERROR, details);
|
||||
}
|
||||
|
||||
public void sendOff() {
|
||||
sendMessage(MESSAGE_OFF, "");
|
||||
}
|
||||
|
||||
public void sendOn() {
|
||||
sendMessage(MESSAGE_ON, "");
|
||||
}
|
||||
|
||||
public void sendRAPDU(String details) {
|
||||
sendMessage(MESSAGE_RAPDU, details);
|
||||
}
|
||||
|
||||
public void sendReset() {
|
||||
sendMessage(MESSAGE_RESET, "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class Settings {
|
||||
|
||||
private static final String KEY_VPCD_HOST = "hostname";
|
||||
private static final String KEY_VPCD_PORT = "port";
|
||||
|
||||
private static final String DEFAULT_VPCD_HOST = "10.0.2.2";
|
||||
private static final String DEFAULT_VPCD_PORT = "35963";
|
||||
|
||||
private final SharedPreferences settings;
|
||||
|
||||
public Settings (Context activity) {
|
||||
settings = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
}
|
||||
|
||||
private void setString(String key, String value) {
|
||||
Editor editor = settings.edit();
|
||||
editor.putString(key, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private void setBoolean(String key, boolean value) {
|
||||
Editor editor = settings.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public void setVPCDHost(String hostname) {
|
||||
setString(KEY_VPCD_HOST, hostname);
|
||||
}
|
||||
|
||||
public void setVPCDPort(String port) {
|
||||
setString(KEY_VPCD_PORT, port);
|
||||
}
|
||||
|
||||
public void setVPCDSettings(String hostname, String port) {
|
||||
Editor editor = settings.edit();
|
||||
editor.putString(KEY_VPCD_HOST, hostname);
|
||||
editor.putString(KEY_VPCD_PORT, port);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public String getVPCDHost() {
|
||||
return settings.getString(KEY_VPCD_HOST, DEFAULT_VPCD_HOST);
|
||||
}
|
||||
|
||||
public String getVPCDPort() {
|
||||
return settings.getString(KEY_VPCD_PORT, DEFAULT_VPCD_PORT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import com.vsmartcard.remotesmartcardreader.app.screaders.SCReader;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class VPCDWorker implements Runnable, Closeable {
|
||||
private int port;
|
||||
private String hostname;
|
||||
private SCReader reader;
|
||||
private Socket socket;
|
||||
private InputStream inputStream;
|
||||
private OutputStream outputStream;
|
||||
private boolean doRun;
|
||||
private MessageSender messageSender;
|
||||
|
||||
public VPCDWorker(String hostname, int port, SCReader reader, Handler handler) {
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
this.reader = reader;
|
||||
this.messageSender = new MessageSender(handler);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
doRun = false;
|
||||
try {
|
||||
vpcdDisconnect();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int VPCD_CTRL_LEN = 1;
|
||||
private static final byte VPCD_CTRL_OFF = 0;
|
||||
private static final byte VPCD_CTRL_ON = 1;
|
||||
private static final byte VPCD_CTRL_RESET = 2;
|
||||
private static final byte VPCD_CTRL_ATR = 4;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
|
||||
doRun = true;
|
||||
|
||||
try {
|
||||
vpcdConnect();
|
||||
messageSender.sendConnected(hostname+":"+Integer.toString(port));
|
||||
|
||||
while (doRun) {
|
||||
byte[] in = receiveFromVPCD();
|
||||
if (in == null) {
|
||||
doRun = false;
|
||||
break;
|
||||
}
|
||||
byte[] out = null;
|
||||
|
||||
if (in.length == VPCD_CTRL_LEN) {
|
||||
switch (in[0]) {
|
||||
case VPCD_CTRL_OFF:
|
||||
reader.powerOff();
|
||||
messageSender.sendOff();
|
||||
break;
|
||||
case VPCD_CTRL_ON:
|
||||
reader.powerOn();
|
||||
messageSender.sendOn();
|
||||
break;
|
||||
case VPCD_CTRL_RESET:
|
||||
reader.reset();
|
||||
messageSender.sendReset();
|
||||
break;
|
||||
case VPCD_CTRL_ATR:
|
||||
out = reader.getATR();
|
||||
//messageSender.sendATR(Hex.getHexString(out));
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unhandled command from VPCD.");
|
||||
}
|
||||
} else {
|
||||
messageSender.sendCAPDU(Hex.getHexString(in));
|
||||
out = reader.transmit(in);
|
||||
messageSender.sendRAPDU(Hex.getHexString(out));
|
||||
}
|
||||
if (out != null) {
|
||||
sendToVPCD(out);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (doRun) {
|
||||
e.printStackTrace();
|
||||
messageSender.sendError(e.getMessage());
|
||||
} else {
|
||||
messageSender.sendDisconnected("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() throws IOException {
|
||||
socket = new Socket(InetAddress.getByName(hostname), port);
|
||||
outputStream = socket.getOutputStream();
|
||||
inputStream = socket.getInputStream();
|
||||
}
|
||||
|
||||
private void vpcdDisconnect() throws IOException {
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app.screaders;
|
||||
|
||||
public class DummyReader implements SCReader {
|
||||
|
||||
@Override
|
||||
public void eject() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean present() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void powerOn() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void powerOff() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
private static final byte[] dummyATR = {0x3B, 0x68, 0x00, (byte) 0xFF, 56, 43, 0x41, 0x52, 0x44, 0x6E, 0x73, 0x73};
|
||||
@Override
|
||||
public byte[] getATR() {
|
||||
return dummyATR;
|
||||
}
|
||||
|
||||
private static final byte[] dummyResponse = {0x6F, 0x00};
|
||||
@Override
|
||||
public byte[] transmit(byte[] apdu) {
|
||||
return dummyResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app.screaders;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.util.Log;
|
||||
|
||||
import com.vsmartcard.remotesmartcardreader.app.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class NFCReader implements SCReader {
|
||||
|
||||
private IsoDep card;
|
||||
|
||||
public NFCReader(IsoDep sc) {
|
||||
this.card = sc;
|
||||
}
|
||||
@Override
|
||||
public void eject() throws IOException {
|
||||
card.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean present() {
|
||||
return card.isConnected();
|
||||
}
|
||||
|
||||
private static int TIMEOUT = 10000;
|
||||
@Override
|
||||
public void powerOn() throws IOException {
|
||||
card.connect();
|
||||
card.setTimeout(TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void powerOff() throws IOException {
|
||||
card.close();
|
||||
}
|
||||
|
||||
private static byte[] SELECT_MF = {(byte) 0x00, (byte) 0xa4, (byte) 0x00, (byte) 0x0C};
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
byte[] response = card.transceive(SELECT_MF);
|
||||
if (response.length == 2 && response[0] == 0x90 && response[1] == 0x00) {
|
||||
Log.d(this.getClass().getName(), "Resetting the card by selecting the MF results in " + Hex.getHexString(response));
|
||||
}
|
||||
}
|
||||
|
||||
/* calculation based on https://code.google.com/p/ifdnfc/source/browse/src/atr.c */
|
||||
@Override
|
||||
public byte[] getATR() {
|
||||
byte[] historicalBytes = card.getHistoricalBytes();
|
||||
if (historicalBytes == null) {
|
||||
historicalBytes = new byte[0];
|
||||
}
|
||||
|
||||
/* copy historical bytes if available */
|
||||
byte[] atr = new byte[4+historicalBytes.length+1];
|
||||
atr[0] = (byte)0x3b;
|
||||
atr[1] = (byte) (0x80+historicalBytes.length);
|
||||
atr[2] = (byte) 0x80;
|
||||
atr[3] = (byte)0x01;
|
||||
System.arraycopy(historicalBytes, 0, atr, 4, historicalBytes.length);
|
||||
|
||||
/* calculate TCK */
|
||||
byte tck = atr[1];
|
||||
for (int idx = 2; idx < atr.length; idx++) {
|
||||
tck ^= atr[idx];
|
||||
}
|
||||
atr[atr.length-1] = tck;
|
||||
|
||||
return atr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transmit(byte[] apdu) throws IOException {
|
||||
return card.transceive(apdu);
|
||||
}
|
||||
|
||||
public static NFCReader processIntent(Intent intent) {
|
||||
NFCReader nfcReader = null;
|
||||
|
||||
if (intent.getExtras() != null) {
|
||||
Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||
if (tag != null) {
|
||||
nfcReader = new NFCReader(IsoDep.get(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return nfcReader;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Frank Morgner
|
||||
*
|
||||
* This file is part of RemoteSmartCardReader.
|
||||
*
|
||||
* RemoteSmartCardReader 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.
|
||||
*
|
||||
* RemoteSmartCardReader 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
|
||||
* RemoteSmartCardReader. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.vsmartcard.remotesmartcardreader.app.screaders;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface SCReader {
|
||||
|
||||
public void eject() throws IOException;
|
||||
|
||||
public boolean present();
|
||||
public void powerOn() throws IOException;
|
||||
public void powerOff() throws IOException;
|
||||
public void reset() throws IOException;
|
||||
|
||||
/**
|
||||
* Receive ATR from the smart card.
|
||||
*
|
||||
* @return ATR on success or null in case of an error
|
||||
*/
|
||||
public byte[] getATR();
|
||||
|
||||
/**
|
||||
* Send an APDU to the smart card
|
||||
*
|
||||
* @param apdu Data to be sent
|
||||
* @return response data or null in case of an error
|
||||
*/
|
||||
public byte[] transmit(byte[] apdu) throws IOException;
|
||||
}
|
||||
Reference in New Issue
Block a user