Android 原生NFC M1卡交互

418 阅读3分钟
/** 

* 请先读以下 * M1 写卡采用NFC MifareClassic技术 * 

* 流程 
* 首先设备要支持NFC权限开启的前提下 不论哪种方式,都是先刷卡,等待系统分发响应的Activity 拿到Tag或者 前台Activity捕获到TAG 。然后根据这个标签支持的技术去解析数据。 *

* M1卡解析 
* 不论是NFC还是读卡模块读,解析流程都是先寻卡,然后验证扇区的密码,取扇区的数据,比如已知要读的数据在2扇区,那么寻卡后验证时把要验证的扇区号、扇区的密码,和扇区的验证密码类型A/B传过去验证通过后,就可以读取数据了 * 

* 扇区 
* 一般16个扇区  每个区 有4块,总共64块 
* 第一扇区的第一块一般用于制造商占用块 
* 0-15个扇区:一个扇区对应4个块,所以总共有64个块,序号分别为0-63,第一个扇区对应:0-3块,第二个扇区对应:4-7块... 
* 每个扇区的最后一个块用来存放密码或控制位,其余为数据块,一个块占用16个字节,keyA占用6字节,控制位占用4字节,keyB占用6字节 * 
* import android.nfc.tech.MifareClassic; 
* MifareClassic类的常用方法 
*      get():根据Tag对象来获得MifareClassic对象; 
*      Connect():允许对MifareClassic标签进行IO操作; 
*      getType():获得MifareClassic标签的具体类型:TYPE_CLASSIC,TYPE_PLUA,TYPE_PRO,TYPE_UNKNOWN 
*      getSectorCount():获得标签总共有的扇区数量; 
*      getBlockCount():获得标签总共有的的块数量; 
*      getSize():获得标签的容量:SIZE_1K,SIZE_2K,SIZE_4K,SIZE_MINI 
*      authenticateSectorWithKeyA(int SectorIndex,byte[] Key):验证当前扇区的KeyA密码,返回值为ture或false。 
*      authenticateSectorWithKeyB(int SectorIndex,byte[] Key):验证当前扇区的KeyB密码,返回值为ture或false。 
*      getBlockCountInSector(int):获得当前扇区的所包含块的数量 *      sectorToBlock(int):当前扇区的第1块的块号; 
*      writeBlock(int,data):将数据data写入当前块;注意:data必须刚好是16Byte,末尾不能用0填充,应该用空格 
*      readBlock(int):读取当前块的数据。 *      close():禁止对标签的IO操作,释放资源。 * 

* MifareClassic标签的读写流程 
*      1,获得Adapter对象 
*      2,获得Tag对象 
*      3,获得MifareClassic对象 
*      4,读取数据块的数据,连接 Connect(),读readBlock(),关闭close() 
*      5,将数据块写入标签,连接 Connect(),读writeBlock(),关闭 close() 

*/

一,获取nfc适配器,判断设备是否支持NFC功能

// nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
    //            shotToast("当前设备不支持NFC功能");        
} else if (!nfcAdapter.isEnabled()) {
    //            shotToast("NFC功能未打开,请先开启后重试!");        
}
//允许扫描的标签类型
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); ndef.addCategory("*/*"); // 允许扫描的标签类型   mWriteTagFilters = new IntentFilter[]{ndef};  mTechLists = new String[][]{         new String[]{                 MifareClassic.class.getName()         },         new String[]{                 NfcA.class.getName()         } };// 允许扫描的标签类型
//开启前台调度系统
nfcAdapter.enableForegroundDispatch(this, pendingIntent, mWriteTagFilters, mTechLists);

二,获取卡ID

tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//通过Tag.getId()方法,获得标签的唯一ID标识String CardId =  ConvertUtil.byteArrayToHexStr(tagFromIntent.getId());

三,获取密钥

四,获取M1卡对象类

mfc =  MifareClassic.get(tag);

五,链接

mfc.connect();

六,验证密钥

// 扇区验证是否成功boolean isAuthenticated = false//sectorIndex:区,hexPs:密钥
if (mfc.authenticateSectorWithKeyA(sectorIndex, hexPsw) ) {   
 isAuthenticated = true;
} else if(mfc.authenticateSectorWithKeyB(sectorIndex, hexPsw) ){  
  isAuthenticated = true;
} else {   
 Log.d("读取扇区"+sectorIndex+"验证失败",pswStr);
    mfc.close();
}

七,读写NFC

//当前扇区的第1块的块号
int block = mfc.sectorToBlock(sectorIndex);

读
byte[] blockByte = mfc.readBlock(i);
写
//blockIndex 写第几块,0-63

//hexText 写的数据mfc.writeBlock(blockIndex,hexText);