Android NFC相关资料之MifareClassic卡(读写)
from: http://wszf.net/archives/2012/11/07/80510.html
一般来说,给予MifareClassic的射频卡,一般内存大小有3种:
1K: 16个分区(sector),每个分区4个块(block),每个块(block) 16个byte数据
2K: 32个分区,每个分区4个块(block),每个块(block) 16个byte数据
4K:64个分区,每个分区4个块(block),每个块(block) 16个byte数据
对于所有基于MifareClassic的卡来说,每个区最后一个块叫Trailer,16个byte, 主要来存放读写该区的key,可以有A,B两个KEY,每个key长6byte,默认的key一般是FF 或 0,最后一个块的内存结构如下:
Block 0 Data 16bytes
Block 1 Data 16 bytes
Block 2 Data 16 bytes
Block 3 Trailer 16 bytes
Trailer:
Key A: 6 bytes
Access Conditions: 4 bytes
Key B: 6 bytes
M1卡分为16个扇区,每个扇区由4块(块0、块1、块2、块3)组成,(我们也将16个扇区的64个块按绝对地址编号为0~63,)存贮结构如右表所示
扇 区 0 |
块0 |
数据块 |
0 |
|
块1 |
数据块 |
1 |
||
块2 |
数据块 |
2 |
||
块3 |
密码A 存取控制 |
数据块 |
3 |
|
扇 区 1 |
块0 |
数据块 |
4 |
|
块1 |
数据块 |
5 |
||
块2 |
数据块 |
6 |
||
块3 |
密码A 存取控制 |
数据块 |
7 |
|
: : : |
||||
扇 区 15 |
块0 |
数据块 |
60 |
|
块1 |
数据块 |
61 |
||
块2 |
数据块 |
62 |
||
块3 |
密码A 存取控制 |
数据块 |
63 |
第0扇区的块0(即绝对地址0块),它用于存放厂商代码,已经固化,不可更改。
每个扇区的块0、块1、块2为数据块,可用于存贮数据。
每个扇区的块3为控制块,包括了密码A、存取控制、密码B。具体结构如下:
A1A2 A3 A4 A5 FF 07 8069 B0 B1 B2 B3 B4 B5 密码A(6字节) 存取控制(4字节) 密码B(6字节) |
每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制;
存取控制为4个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的。
工作原理:
读写器向M1卡发一组固定频率的电磁波,卡片内有一个LC串联谐振电路,其频率与讯写器发射的频率相同,在电磁波的激励下,LC谐振电路产生共振,从而使电容内有了电荷,在这个电容的另一端,接有一个单向导通的电子泵,将电容内的电荷送到另一个电容内储存,当所积累的电荷达到2V时,此电容可做为电源为其它电路提供工作电压,将卡内数据发射出去或接取读写器的数据。
Android 读写M1卡
先了解一下MifareClassic协议
在android sdk 的文档中,描述道 “all MifareClassic I/O operations will be supported, and MIFARE_CLASSIC NDEF tags will also be supported. In either case, NfcAwill also be enumerated on the tag, because all MIFARE Classic tags are also NfcA.” 所以说NFCA协议是兼容MifareClassic 协议的, 我们可以通过NfcA在android的相关类来处理给予MifareClassic 的RFID卡。
读M1卡代码:
01 |
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) |
|
02 |
{ |
03 |
// 3) Get an instance of the TAG from the NfcAdapter |
|
04 |
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); |
05 |
// 4) Get an instance of the Mifare classic card from this TAG |
|
06 |
// intent |
07 |
mfc = MifareClassic.get(tagFromIntent); |
|
08 |
if(mfc!=null) |
09 |
{ |
|
10 |
Toast.makeText(this, “检测到卡片,读卡中。。。”, Toast.LENGTH_SHORT).show(); |
11 |
try |
|
12 |
{ |
13 |
mfc.connect(); |
|
14 |
boolean auth = false; |
15 |
auth = mfc.authenticateSectorWithKeyA(15, “passwo”.getBytes());//验证密码 |
|
16 |
if (auth) |
17 |
{ |
||
18 |
card_number_edittext.setText(new String(mfc.readBlock(60)));//读取M1卡的第60块即15扇区第0块 |
19 |
password_edittext.requestFocus(); |
||
20 |
} |
21 |
} catch (Exception e) |
|
22 |
{ |
23 |
if(BuildConfig.DEBUG) |
|
24 |
{ |
25 |
e.printStackTrace(); |
||
26 |
} |
27 |
|
|
28 |
} |
29 |
} |
30 |
}// End of method |
写M1卡代码:
01 |
try |
|
02 |
{ |
03 |
mfc.connect(); |
|
04 |
boolean auth = false; |
05 |
short sectorAddress = 1; |
|
06 |
auth = mfc.authenticateSectorWithKeyA(sectorAddress, MifareClassic.KEY_DEFAULT); |
07 |
if (auth) |
|
08 |
{ |
09 |
// the last block of the sector is used for KeyA and KeyB cannot be overwritted |
10 |
mfc.writeBlock(4, “1383838438000000”.getBytes());//必须为16字节不够自己补0 |
11 |
mfc.close(); |
|
12 |
Toast.makeText(MainActivity.this, “写入成功”, Toast.LENGTH_SHORT).show(); |
13 |
} |
|
14 |
} catch (IOException e) |
15 |
{ |
|
16 |
// TODO Auto-generated catch block |
17 |
e.printStackTrace(); |
|
18 |
} finally |
19 |
{ |
|
20 |
try |
21 |
{ |
|
22 |
mfc.close(); |
23 |
} catch (IOException e) |
|
24 |
{ |
25 |
// TODO Auto-generated catch block |
|
26 |
e.printStackTrace(); |
27 |
} |
|
28 |
} |
Demo下载地址:NFCDemo