马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
利用安卓手机,通过NFC功能可以读取IC卡信息,。
IC卡有很多种卡标准,协议,等等,这些具体就不细讨论。
主要讨论,2种卡,一种是M1卡,别的是CPU卡。
1、 M1卡
M1卡是市面上比较常见的卡,一般的门禁卡。比较便宜。
M1卡的数据存储比较通用的是分扇区存储数据。默认是16个扇区,每个扇区4个数据块。
每个卡片都有一个ID号码(去买很多的卡的时间,可以要求厂商提供给你的每个卡ID是唯一的。)
0扇区的数据,是厂商写死的数据,包含卡面的ID号。
其他扇区的数据,可以自己去写,可以加密写。(密码为12位)
2、 CPU卡
CPU卡是加密卡,CPU卡还分为双界面卡和单界面卡。比较复杂。此次只说明,CPU卡的数据,通常是通过指令来读取数据。
3、android 的NFC功能
当安卓手机的NFC功能感应到 卡片的时间。会读取出该卡片支持的数据传输方式
android.nfc.tech.NfcA
android.nfc.tech.NfcB
android.nfc.tech.MifareClassic
android.nfc.tech.IsoDep
1、 平凡的M1卡,会支持好几种数据传输方式,一般会有NfcA,MifareClassic等。如果支持MifareClassic,那就优先利用这种数据传输方式,获取数据。
2、 CPU卡的数据传输,会支持NfcA,IsoDep等,优先利用IsoDep
3、现在市面上出现了云解身份证,原理是通过NFC读取身份证的加密信息,通过API调用公安接口返回用户的证件信息。身份证通过NFC读取,只能通过NFCB的模式读取数据。
安卓开启NFC功能,肯定先开启权限
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools">
- <application
- android:allowBackup="true"
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/Theme.Smkpda"
- tools:targetApi="31">
- <activity
- android:name=".MainActivity"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- <!--NFC权限-->
- <uses-permission android:name="android.permission.NFC" />
- <!-- 要求当前设备必须要有NFC芯片 -->
- <uses-feature android:name="android.hardware.nfc" android:required="true" />
- </manifest>
复制代码 4、定义一个单例的NfcUtils 工具类
- package com.sss.ssspda.common.nfc;
-
- import android.app.Activity;
- import android.content.Context;
- import android.nfc.NfcAdapter;
- import android.nfc.Tag;
- import android.util.Log;
- import android.widget.Toast;
-
- /**
- * Created by Administrator on 2023/11/10.
- */
-
- public class NfcUtils {
- private static final String TAG = "NfcUtils";
-
- private static NfcAdapter mNfcAdapter;
-
- //NFC功能,只能定义为单例模式,避免出现系统奔溃
- private NfcUtils(){}
-
- private static NfcUtils nfcUtils = null;
-
- private static boolean isOpen = false;
-
- /**
- * 获取NFC的单例
- * @return NfcUtils
- */
- public static NfcUtils getInstance(){
- if (nfcUtils == null){
- synchronized (NfcUtils.class){
- if (nfcUtils == null){
- nfcUtils = new NfcUtils();
- }
- }
- }
- return nfcUtils;
- }
-
- /**
- * 在onStart中检测是否支持nfc功能
- * @param context 当前页面上下文
- */
- public void onStartNfcAdapter(Context context){
- mNfcAdapter = NfcAdapter.getDefaultAdapter(context);//设备的NfcAdapter对象
- if(mNfcAdapter==null){//判断设备是否支持NFC功能
- Toast.makeText(context,"设备不支持NFC功能!",Toast.LENGTH_SHORT).show();
- return;
- }
- if (!mNfcAdapter.isEnabled()){//判断设备NFC功能是否打开
- Toast.makeText(context,"请到系统设置中打开NFC功能!",Toast.LENGTH_SHORT).show();
- return;
- }
- Log.d(TAG,"NFC is start");
- }
-
- /**
- * 在onResume中开启nfc功能
- * @param activity
- */
- public void onResumeNfcAdapter(final Activity activity){
- if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) {
- throw new RuntimeException("NFC is not start");
- }
- // mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,null,null);//打开前台发布系统,使页面优于其它nfc处理.当检测到一个Tag标签就会执行mPendingItent
- if (!isOpen) {
- mNfcAdapter.enableReaderMode(activity, new NfcAdapter.ReaderCallback() {
- @Override
- public void onTagDiscovered(final Tag tag) {
- //ByteArrayToHexString(tag.getId())即cardId
- if (nfcListener != null)
- (activity).runOnUiThread(new Runnable() {
- @Override
- public void run() {
- nfcListener.doing(tag);
- }
- });
- }
- },
- (NfcAdapter.FLAG_READER_NFC_A |
- NfcAdapter.FLAG_READER_NFC_B |
- NfcAdapter.FLAG_READER_NFC_F |
- NfcAdapter.FLAG_READER_NFC_V |
- NfcAdapter.FLAG_READER_NFC_BARCODE ),
- null);
- isOpen = true;
- Log.d(TAG, "Resume");
- }
- }
-
- /**
- * 在onPause中关闭nfc功能
- * @param activity
- */
- public void onPauseNfcAdapter(Activity activity){
- if(mNfcAdapter!=null && mNfcAdapter.isEnabled()){
- if (isOpen){
- mNfcAdapter.disableReaderMode(activity);
- }
- isOpen = false;
- }
- Log.d("myNFC","onPause");
- }
-
- private NfcListener nfcListener;
-
- public void setNfcListener(NfcListener listener){
- nfcListener = listener;
- }
- public String ByteArrayToHexString(byte[] inarray) {
- int i, j, in;
- String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
- "B", "C", "D", "E", "F"};
- String out = "";
- for (j = 0; j < inarray.length; ++j) {
- in = (int) inarray[j] & 0xff;
- i = (in >> 4) & 0x0f;
- out += hex[i];
- i = in & 0x0f;
- out += hex[i];
- }
- return out;
- }
- }
复制代码 定义一个处理读卡变乱的接口类
NfcListener
- package com.sss.ssspda.common.nfc;
- import android.nfc.Tag;
- /**
- * 自定义的NFC接口
- */
- public interface NfcListener{
- /**
- * 用于扫到nfc后的后续操作
- */
- void doing(Tag tag);
- }
复制代码 实现获取卡片数据交互
- package com.sss.ssspda.common.nfc;
- import android.nfc.Tag;
- import android.nfc.tech.IsoDep;
- import android.nfc.tech.MifareClassic;
- import android.nfc.tech.NfcB;
- import android.widget.Toast;
- import java.io.IOException;
- public class NfcReadHander {
- /**
- * 读M1卡的方法扇区,MifareClassic 类型
- * @return
- *
- */
- public static String readMifareTag(Tag tag, MifareClassic mfc){
- //读取TAG
- try {
- String metaInfo = "";
- //操作之前,一点要先连接卡片通讯
- mfc.connect();
- int type = mfc.getType();//获取TAG的类型
- int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
- String typeS = "";
- switch (type) {
- case MifareClassic.TYPE_CLASSIC:
- typeS = "TYPE_CLASSIC";
- break;
- case MifareClassic.TYPE_PLUS:
- typeS = "TYPE_PLUS";
- break;
- case MifareClassic.TYPE_PRO:
- typeS = "TYPE_PRO";
- break;
- case MifareClassic.TYPE_UNKNOWN:
- typeS = "TYPE_UNKNOWN";
- break;
- }
- metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共" + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n";
- System.out.println(metaInfo);
- int blockIndex;
- for(int i =0;i<16;i++){
- try{
- System.out.println(i);
- metaInfo += "扇区:"+i+",key:";
- boolean f = false;
- byte[] keybyte = new byte[]{0x01, (byte) 0x11, (byte) 0x18,0x3F,0x12, (byte) 0x11};//样例的一个密码,要自己去获取,卡片商获取
- //每个扇区的数据读取都可能不一样,每个扇区有的有数据,有的是加密数据,有的是空数据
- //没加密扇区都会配置一个默认密码,开发包里面默认带有3种默认密码,
- //这里代码只列出来使用A加密的方法,也有B加密方法authenticateSectorWithKeyB,要根据实际卡片加密方法来设置
- if(mfc.authenticateSectorWithKeyA(i,MifareClassic.KEY_DEFAULT)){
- metaInfo += "KEY_DEFAULT";
- f = true;
- }else if(mfc.authenticateSectorWithKeyA(i,MifareClassic.KEY_NFC_FORUM)){
- metaInfo += ".KEY_NFC_FORUM";
- f = true;
- }else if(mfc.authenticateSectorWithKeyA(i,MifareClassic.KEY_MIFARE_APPLICATION_DIRECTORY)){
- metaInfo += ".KEY_MIFARE_APPLICATION_DIRECTORY";
- f = true;
- }else if(mfc.authenticateSectorWithKeyA(i,keybyte)){
- //这里的密码 keybyte,要自己和卡片提供商来获取,有可能是一个批次的卡片的密码都一样,也可能每个卡片的密码都不一样,这里的密码需要自己想办法获取。
- metaInfo += ByteArrayToHexString(keybyte);
- f = true;
- }else{
- }
- metaInfo += "\n";
- if(f){
- //前面通过扇区界面进入了扇区里面,这里就是计算当前扇区的第一个块的块号码
- int bnum = mfc.sectorToBlock(i);
- //通过块号码读取块里面的数据
- metaInfo += ByteArrayToHexString(mfc.readBlock(bnum))+"\n";
- //每个扇区有4个块,依次读取
- metaInfo +=ByteArrayToHexString(mfc.readBlock(bnum+1))+"\n";
- metaInfo +=ByteArrayToHexString(mfc.readBlock(bnum+2))+"\n";
- metaInfo +=ByteArrayToHexString(mfc.readBlock(bnum+3))+"\n";
- }
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- return metaInfo;
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (mfc != null) {
- try {
- mfc.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return null;
- }
- public static String ByteArrayToHexString(byte[] inarray) {
- int i, j, in;
- String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
- "B", "C", "D", "E", "F"};
- String out = "";
- for (j = 0; j < inarray.length; ++j) {
- in = (int) inarray[j] & 0xff;
- i = (in >> 4) & 0x0f;
- out += hex[i];
- i = in & 0x0f;
- out += hex[i];
- }
- return out;
- }
- //找卡片提供商获取的读卡指令
- private static byte[] sheb = new byte[]{0x00, (byte) 0x14,0x04,0x00,0x0F,0x13,0x18,0x11,0x1E,0x13,0x18,0x2E,(byte) 0x19,(byte) 0xE1,(byte) 0xB1,(byte) 0xE1,(byte) 0x11
- ,(byte) 0x13,(byte) 0x15,(byte) 0xCF };
- //找卡片提供商获取的读卡指令
- private static byte[] JIAOTONG = new byte[]{0x00, (byte) 0x14, (byte) 0x04, (byte) 0x00, (byte) 0x18, (byte) 0x10, (byte) 0x10, (byte) 0x00
- , (byte) 0x06, (byte) 0x12, (byte) 0x11, (byte) 0x11, (byte) 0x15};
-
- //通过ISODEP读取数据
- public static String readIsoDepTag(Tag tag, IsoDep isodep) {
- String msg = "";
- try{
- //先连接
- isodep.connect();
- if(isodep.isConnected()){
- //判断是否链接成功
- }
- msg += "sheb指令读取的数据:\n";
- msg += transcMsg(isodep,sheb)+"\n";
- msg += "JIAOTONG指令读取的数据:\n";
- msg += transcMsg(isodep,JIAOTONG)+"\n";
-
- }catch (Exception e){
- }
- return msg;
- }
- public static String transcMsg(IsoDep isodep,byte[] b) throws Exception{
- return ByteArrayToHexString(isodep.transceive(b));
- }
- //nfcB类型,身份证就是此种传输方式
- public static String readNfcBTag(Tag tag, NfcB nfcb) {
- String msg = "";
- try{
- nfcb.connect();
- msg += ByteArrayToHexString(nfcb.getApplicationData()) +"\n";
- byte[] sd = new byte[]{0x05,0x00,0x00};
- msg += ByteArrayToHexString(nfcb.transceive(sd)) +"\n";
- msg += ByteArrayToHexString(nfcb.getApplicationData()) +"\n";
- }catch (Exception e){
- e.printStackTrace();
- }
- return msg;
- }
- }
复制代码 调用主类
- package com.sss.ssspda;
- import android.nfc.Tag;
- import android.nfc.tech.IsoDep;
- import android.nfc.tech.MifareClassic;
- import android.nfc.tech.NfcA;
- import android.nfc.tech.NfcB;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.TextView;
- import android.widget.Toast;
- import androidx.activity.EdgeToEdge;
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.core.graphics.Insets;
- import androidx.core.view.ViewCompat;
- import androidx.core.view.WindowInsetsCompat;
- import com.sss.ssspda.common.nfc.NfcListener;
- import com.sss.ssspda.common.nfc.NfcReadHander;
- import com.sss.ssspda.common.nfc.NfcUtils;
- public class MainActivity extends AppCompatActivity implements NfcListener {
- private TextView typetext;
- private TextView datatext;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- EdgeToEdge.enable(this);
- setContentView(R.layout.activity_main);
- ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
- Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
- v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
- return insets;
- });
- typetext = findViewById(R.id.typenfc);
- datatext = findViewById(R.id.data);
- nfcUtils.setNfcListener(this);
- Log.d("myNFC","onPause");
- }
- public void Welcome1(View view) {
- Toast.makeText(this, "按钮点击一下", Toast.LENGTH_SHORT).show();
- }
- public void Welcome2(View view) {
- Toast.makeText(this, "撮了一下", Toast.LENGTH_SHORT).show();
- }
- private NfcUtils nfcUtils = NfcUtils.getInstance();
- private TextView textView;
- @Override
- protected void onStart() {
- super.onStart();
- System.out.println("onStart................................");
- nfcUtils.onStartNfcAdapter(this); //初始化Nfc对象
- }
- @Override
- protected void onResume() {
- super.onResume();
- System.out.println("onResume................................");
- nfcUtils.onResumeNfcAdapter(this); //activity激活的时候开始扫描
- }
- @Override
- protected void onPause() {
- super.onPause();
- System.out.println("onPause................................");
- nfcUtils.onPauseNfcAdapter(this); //activity切换到后台的时候停止扫描
- }
- @Override
- public void doing(Tag tag) {
- String tl[] = tag.getTechList();
- for (String s:tl) {
- System.out.println(s);
- }
- for (String s:tl) {
- System.out.println("type:"+s);
- if(s.equals("android.nfc.tech.MifareClassic")){
- typetext.setText("MifareClassic");
- String data = NfcReadHander.readMifareTag(tag,MifareClassic.get(tag));
- datatext.setText(data);
- break;
- }else if(s.equals("android.nfc.tech.IsoDep")){
- typetext.setText("IsoDep");
- String data = NfcReadHander.readIsoDepTag(tag, IsoDep.get(tag));
- datatext.setText(data);
- break;
- }else if(s.equals("android.nfc.tech.NfcB")){
- typetext.setText("NfcB");
- String data = NfcReadHander.readNfcBTag(tag, NfcB.get(tag));
- datatext.setText(data);
- break;
- }
- }System.out.println("onPause................................"+NfcReadHander.ByteArrayToHexString(tag.getId()));
-
- }
复制代码 结构文件
- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/main"
- android:layout_width="match_parent"
- android:layout_height="900sp"
- android:onClick="Welcome1"
- tools:context=".MainActivity">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="1054dp"
- android:orientation="vertical"
- app:layout_constraintTop_toTopOf="parent">
- <TextView
- android:id="@+id/textView1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="15dp"
- android:layout_marginTop="15dp"
- android:layout_marginRight="15dp"
- android:layout_marginBottom="15dp"
- android:background="#77CCB3"
- android:text="读取类型"
- android:textAlignment="center"
- android:textSize="35sp" />
- <TextView
- android:id="@+id/typenfc"
- android:layout_width="match_parent"
- android:layout_height="77dp"
- android:text="type"
- android:textSize="25sp" />
- <TextView
- android:id="@+id/textView2"
- android:layout_width="match_parent"
- android:layout_height="59dp"
- android:background="#6DCFC6"
- android:text="数据"
- android:textAlignment="center"
- android:textSize="35sp" />
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="613dp">
- <TextView
- android:id="@+id/data"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="TextView"
- android:textSize="15sp" />
- </ScrollView>
- </LinearLayout>
- </androidx.constraintlayout.widget.ConstraintLayout>
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |