Dieser Artikel ist der 8. Tagesartikel von SLP KBIT Adventskalender 2019.
Kürzlich habe ich erfahren, dass Sie die Daten auf der IC-Karte mit der NFC-Funktion von Android lesen können. Also dachte ich: "Kann ich meinen Studentenausweis lesen?", Also versuchte ich es.
Dieses Mal möchte ich den IC-Kontostand der Universitätsgenossenschaft lesen, der im Studentenausweis gespeichert ist.
Da es sich bei Ihrem Studentenausweis um einen mit Felica kompatiblen IC-Ausweis handelt, können Sie ihn mit einem mit NFC Felica kompatiblen Terminal lesen. Der Felica-Standard ist in Auszug aus dem FeliCa Card-Benutzerhandbuch zusammengefasst.
In Bezug auf die Datenstruktur des Studentenausweises habe ich darauf verwiesen, wie sie in Spezifikationen der Universitätsgenossenschaft Felica zusammengefasst wurde.
Die Daten, die ich dieses Mal erhalten möchte, sind
Es wird als ** Purse Service ** in gespeichert. Gemäß der Purse Service-Spezifikation wird der tatsächliche Kontostand in den oberen 4 Bytes im Little-toilen-Format gespeichert. Daher müssen nach dem Erfassen der Daten 4 Bytes extrahiert und in das Big-Endian-Format konvertiert werden.
Grob zusammengefasst hat es die folgende Struktur.
- System 0:
- ...
- System 1: (0xFE00)
- IDm
- Area
- ...
- Area
- Service
- ...
- Purse Service: (0x50D7) <-Hier
Der Gesamtprozess ist MainActivity.java, Das Abrufen von Daten von Felica erfolgt mit NfcReader.java.
Informationen zur von MainActivity.java durchgeführten Verarbeitung finden Sie auf der folgenden Seite. [Android] Lesen Sie NFC: Crafting and Horse Racing --Livedoor
Der eindeutige Wert der Karte wird konstant gemacht, falls Sie eine andere Karte oder Daten lesen möchten.
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.widget.Toast;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity {
//Konstante
private final byte[] TARGET_SYSTEM_CODE = new byte[]{(byte) 0xFE, (byte) 0x00};
private final byte[] TARGET_SERVICE_CODE = new byte[]{(byte) 0x50, (byte) 0xD7};
private final int TARGET_SIZE = 1;
//Variablen für den Umgang mit Adaptern
private NfcAdapter mNfcAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Holen Sie sich eine Instanz des Adapters
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
}
@Override
protected void onResume() {
super.onResume();
//Einstellungen, wenn der NFC gehalten wird
Intent intent = new Intent(this, this.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//Vermeiden Sie das Öffnen anderer Apps
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
@Override
protected void onPause() {
super.onPause();
//Nicht empfangen, wenn die Aktivität im Hintergrund ist
mNfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onNewIntent(Intent intent) {
NfcReader nfcReader = new NfcReader();
super.onNewIntent(intent);
//Holen Sie sich NFC TAG
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//Bilanzdaten lesen
byte[][] data = nfcReader.readTag(tag, TARGET_SYSTEM_CODE, TARGET_SERVICE_CODE, TARGET_SIZE);
//Wenn es nicht gelesen werden kann, endet es
if (data == null) {
Toast.makeText(this, "error", Toast.LENGTH_SHORT).show();
return;
}
//Umwandlung
byte[] balanceData = new byte[]{data[0][3], data[0][2], data[0][1], data[0][0]};
int balance = ByteBuffer.wrap(balanceData).getInt();
//Anzeige
Toast.makeText(this, balance + "Kreis", Toast.LENGTH_LONG).show();
}
}
Erstellen Sie als Nächstes NfcReader.java. NfcReader.java basiert auf dem Code auf der folgenden Seite. Felica (NFC) -Blockdaten auf Android abrufen
Der Verarbeitungsinhalt ist nahezu identisch, die readTag-Methode wurde jedoch geändert, sodass der eindeutige Wert der Karte als Argument übergeben werden kann.
NfcReader.java
import android.nfc.Tag;
import android.nfc.tech.NfcF;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import static android.content.ContentValues.TAG;
class NfcReader {
public byte[][] readTag(Tag tag, byte[] targetSystemCode, byte[] targetServiceCode, int size) {
NfcF nfc = NfcF.get(tag);
if (nfc == null) {
return null;
}
try {
nfc.connect();
//Abrufbefehl erstellen
byte[] polling = polling(targetSystemCode);
//Senden Sie einen Befehl und erhalten Sie das Ergebnis
byte[] pollingRes = nfc.transceive(polling);
//Holen Sie sich System 0 IDm(Das erste Byte ist die Datengröße, das zweite Byte ist der Antwortcode und die IDm-Größe beträgt 8 Bytes.)
byte[] targetIDm = Arrays.copyOfRange(pollingRes, 2, 10);
//Befehl "Lesen ohne Verschlüsselung" erstellen
byte[] req = readWithoutEncryption(targetIDm, size, targetServiceCode);
//Senden Sie einen Befehl und erhalten Sie das Ergebnis
byte[] res = nfc.transceive(req);
nfc.close();
//Analysieren Sie das Ergebnis und erhalten Sie nur die Daten
return parse(res);
} catch (Exception e) {
Log.e(TAG, e.getMessage() , e);
}
return null;
}
/*
*Abrufbefehl abrufen.
* @param systemCode byte[]Systemcode anzugeben
* @Befehl Polling zurückgeben
* @throws IOException
*/
private byte[] polling(byte[] systemCode) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(100);
bout.write(0x00); //Datenlängen-Byte-Dummy
bout.write(0x00); //Befehlscode
bout.write(systemCode[0]); // systemCode
bout.write(systemCode[1]); // systemCode
bout.write(0x01); //Anfrage Code
bout.write(0x0f); //Zeitfenster
byte[] msg = bout.toByteArray();
msg[0] = (byte) msg.length; //Das erste Byte ist die Datenlänge
return msg;
}
/*
*Rufen Sie den Befehl Lesen ohne Verschlüsselung ab.
* @param IDm ID des angegebenen Systems
* @Parametergröße Anzahl der abzurufenden Daten
* @Befehl Read Without Encryption zurückgeben
* @throws IOException
*/
private byte[] readWithoutEncryption(byte[] idm, int size, byte[] serviceCode) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream(100);
bout.write(0); //Datenlängen-Byte-Dummy
bout.write(0x06); //Befehlscode
bout.write(idm); // IDm 8byte
bout.write(1); //Länge der Dienstnummer(Die folgenden 2 Bytes wiederholen sich für diese Anzahl von Minuten)
//Da der Servicecode als Little Endian angegeben ist, geben Sie ihn im unteren Byte an.
bout.write(serviceCode[1]); //Service Code Low Byte
bout.write(serviceCode[0]); //Service Code High Byte
bout.write(size); //Anzahl der Blöcke
//Angabe der Blocknummer
for (int i = 0; i < size; i++) {
bout.write(0x80); //Blockelement oberes Byte "Felica Benutzerhandbuch Auszug" 4.Siehe Punkt 3
bout.write(i); //Blocknummer
}
byte[] msg = bout.toByteArray();
msg[0] = (byte) msg.length; //Das erste Byte ist die Datenlänge
return msg;
}
/*
*Analyse ohne Verschlüsselungsantwort lesen.
* @param res byte[]
* @Zeichenfolgendarstellung zurückgeben
* @throws Exception
*/
private byte[][] parse(byte[] res) {
// res[10]Fehlercode. 0x00 ist normal
if (res[10] != 0x00) {
throw new RuntimeException("Read Without Encryption Command Error");
}
// res[12]Anzahl der Antwortblöcke
// res[13 + n * 16]Aktuelle Daten 16(byte/Block)Wiederholen
int size = res[12];
byte[][] data = new byte[size][16];
for (int i = 0; i < size; i++) {
byte[] tmp = new byte[16];
int offset = 13 + i * 16;
for (int j = 0; j < 16; j++) {
tmp[j] = res[offset + j];
}
data[i] = tmp;
}
return data;
}
}
Als ich meinen Studentenausweis über mein Smartphone hielt, wurde der Kontostand genau angezeigt. Es stimmte sogar im Vergleich zur Quittung überein. War gut.
Es war meine erste Entwicklung auf Android, aber es war interessanter als ich erwartet hatte. Android hat verschiedene Funktionen, deshalb wollte ich verschiedene Dinge ausprobieren, wenn ich Zeit hatte.
Recommended Posts