Cet article est l'article du 8ème jour du SLP KBIT Advent Calendar 2019.
Récemment, j'ai appris que vous pouvez lire les données sur la carte IC en utilisant la fonction NFC d'Android. Alors j'ai pensé: «Puis-je lire ma carte d'étudiant?», Alors j'ai essayé.
Cette fois, j'aimerais lire le solde IC de la coopérative universitaire stocké dans la carte d'étudiant.
La carte d'étudiant dont vous disposez est une carte IC compatible avec Felica, vous pouvez la lire avec un terminal compatible avec NFC Felica. La norme Felica est résumée dans Extrait du manuel d'utilisation de la carte FeliCa.
En ce qui concerne la structure des données de la carte d'identité d'étudiant, elle a été résumée dans Spécifications de la coopérative universitaire Felica, alors j'y ai fait référence.
Les données que je veux obtenir cette fois sont
--Code système ** 0xFE00 ** --Code de service ** 0x50D7 **
Il est stocké sous le nom de ** Purse Service ** dans. Selon les spécifications du Purse Service, le solde réel est stocké dans les 4 octets supérieurs au format little-toilen. Par conséquent, il est nécessaire d'extraire 4 octets après l'acquisition des données et de les convertir au format big endian.
En gros, il a la structure suivante.
- System 0:
- ...
- System 1: (0xFE00)
- IDm
- Area
- ...
- Area
- Service
- ...
- Purse Service: (0x50D7) <-ici
Le processus global est MainActivity.java, Le processus de récupération des données de Felica se fait avec NfcReader.java.
Pour le traitement effectué par MainActivity.java, reportez-vous à la page suivante. [Android] Lisez NFC: Artisanat et courses de chevaux --Livedoor
La valeur unique de la carte est rendue constante au cas où vous voudriez lire une autre carte ou des données.
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 {
//constant
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;
//Variables de manipulation des adaptateurs
private NfcAdapter mNfcAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Obtenez une instance de l'adaptateur
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
}
@Override
protected void onResume() {
super.onResume();
//Paramètres lorsque le NFC est suspendu
Intent intent = new Intent(this, this.getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//Évitez d'ouvrir d'autres applications
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
@Override
protected void onPause() {
super.onPause();
//Ne pas recevoir lorsque l'activité est en arrière-plan
mNfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onNewIntent(Intent intent) {
NfcReader nfcReader = new NfcReader();
super.onNewIntent(intent);
//Obtenez NFC TAG
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//Lire les données de balance
byte[][] data = nfcReader.readTag(tag, TARGET_SYSTEM_CODE, TARGET_SERVICE_CODE, TARGET_SIZE);
//S'il ne peut pas être lu, il se termine
if (data == null) {
Toast.makeText(this, "error", Toast.LENGTH_SHORT).show();
return;
}
//conversion
byte[] balanceData = new byte[]{data[0][3], data[0][2], data[0][1], data[0][0]};
int balance = ByteBuffer.wrap(balanceData).getInt();
//afficher
Toast.makeText(this, balance + "Cercle", Toast.LENGTH_LONG).show();
}
}
Ensuite, créez NfcReader.java. NfcReader.java est basé sur le code de la page suivante. Obtention des données de bloc Felica (NFC) sur Android
Le contenu du traitement est presque le même, mais la méthode readTag a été modifiée pour que la valeur unique de la carte puisse être passée en argument.
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();
//Créer une commande d'interrogation
byte[] polling = polling(targetSystemCode);
//Envoyez une commande et obtenez le résultat
byte[] pollingRes = nfc.transceive(polling);
//Obtenir l'ID système 0(Le premier octet est la taille des données, le deuxième octet est le code de réponse et la taille IDm est de 8 octets.)
byte[] targetIDm = Arrays.copyOfRange(pollingRes, 2, 10);
//Commande Créer une lecture sans chiffrement
byte[] req = readWithoutEncryption(targetIDm, size, targetServiceCode);
//Envoyez une commande et obtenez le résultat
byte[] res = nfc.transceive(req);
nfc.close();
//Analysez le résultat et obtenez uniquement les données
return parse(res);
} catch (Exception e) {
Log.e(TAG, e.getMessage() , e);
}
return null;
}
/*
*Obtenez la commande d'interrogation.
* @param systemCode byte[]Code système à spécifier
* @return Commande d'interrogation
* @throws IOException
*/
private byte[] polling(byte[] systemCode) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(100);
bout.write(0x00); //Dummy de longueur de données
bout.write(0x00); //Code de commande
bout.write(systemCode[0]); // systemCode
bout.write(systemCode[1]); // systemCode
bout.write(0x01); //Code requis
bout.write(0x0f); //Créneau horaire
byte[] msg = bout.toByteArray();
msg[0] = (byte) msg.length; //Le premier octet est la longueur des données
return msg;
}
/*
*Obtenez la commande Lire sans chiffrement.
* @param IDm ID du système spécifié
* @param size Nombre de données à récupérer
* @return Read Without Encryption, commande
* @throws IOException
*/
private byte[] readWithoutEncryption(byte[] idm, int size, byte[] serviceCode) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream(100);
bout.write(0); //Dummy de longueur de données
bout.write(0x06); //Code de commande
bout.write(idm); // IDm 8byte
bout.write(1); //Numéro d'ancienneté(Les 2 octets suivants se répètent pendant ce nombre de minutes)
//Étant donné que le code de service est spécifié comme Little Endian, spécifiez-le à partir de l'octet inférieur.
bout.write(serviceCode[1]); //Code de service octet de poids faible
bout.write(serviceCode[0]); //Code de service octet de poids fort
bout.write(size); //Nombre de blocs
//Spécifier le numéro de bloc
for (int i = 0; i < size; i++) {
bout.write(0x80); //Élément de bloc octet supérieur "Extrait du manuel d'utilisation Felica" 4.Voir l'élément 3
bout.write(i); //Numéro de bloc
}
byte[] msg = bout.toByteArray();
msg[0] = (byte) msg.length; //Le premier octet est la longueur des données
return msg;
}
/*
*Lire sans analyse de réponse de chiffrement.
* @param res byte[]
* @retourne la représentation sous forme de chaîne
* @throws Exception
*/
private byte[][] parse(byte[] res) {
// res[10]Code d'erreur. 0x00 est normal
if (res[10] != 0x00) {
throw new RuntimeException("Read Without Encryption Command Error");
}
// res[12]Nombre de blocs de réponse
// res[13 + n * 16]Données réelles 16(byte/bloquer)Répéter
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;
}
}
Lorsque je tenais ma carte d'étudiant sur mon smartphone, le solde était affiché avec précision. Il correspondait même par rapport au reçu. C'était bien.
C'était la première fois que je développais sur Android, mais c'était plus intéressant que ce à quoi je m'attendais. Android a diverses fonctions, donc je voulais essayer diverses choses si j'avais le temps.
Recommended Posts