扇区概念
常见的基于 Mifare Classic 技术的 NFC 卡,如 M1 卡 ,有 16 个扇区 。每个扇区由 4 个块(块 0、块 1、块 2、块 3 )组成 。其中,第 0 扇区的块 0 一般用于存放厂商代码等固化信息,不可更改;其他扇区的块 0、块 1、块 2 多为数据块,用于存储用户数据 ;块 3 为控制块,包含密码 A、存取控制信息、密码 B 。
全局块
将每个扇区的块放在一起,一共是64块,一个扇区有四块,第一扇区就是0-3,第二扇区就是4-7,以此类推。
全局控制块的计算方法
扇区*4+3
以第1扇区为例,1*4+3=7,7是控制块,4-5-6是数据块。
读写步骤
获取NFC实例-->发现卡-->认证扇区块-->读写数据
代码示例
一些细节都在代码里面注释
import Toast from '@vant/weapp/toast/toast';
let that = null
let nfcAdapter = null
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
that = this
this.checkPhoneNfc()
},
checkPhoneNfc() {
let that = this
//获取NFC实例
nfcAdapter = wx.getNFCAdapter()
//开启NFC
nfcAdapter.startDiscovery({
success(res) {
//vantUI 没有安装的可以去掉,或者自己安装一下
Toast.loading({
message: "请刷卡",
forbidClick: true,
duration: 0
})
//监听NFC刷卡
nfcAdapter.onDiscovered(res => {
//一般都会有MIFARE Classic
if (res.techs.includes("MIFARE Classic")) {
//这里使用getMifareClassic,还有getNfcA/getNfcB,但是流程都是一样的,按照实际情况来就可以了
let nfcMAdapter = nfcAdapter.getMifareClassic()
//传递实例以及id,id在认证的时候需要用到
that.nfcWirteCard(nfcMAdapter, new Uint8Array(res.id))
} else {
Toast("当前卡片不支持写入格式")
}
});
},
fail(err) {
if (err.errCode == 13001) {
Toast("系统NFC开关未开启")
}
if (err.errCode == 13000) {
Toast("当前设备不支持NFC")
}
}
})
},
nfcWirteCard(nfcMAdapter, uid) {
//连接卡片
nfcMAdapter.connect()
//连接上了,会立刻调用
nfcMAdapter.isConnected({
success(res) {
/*
以下代码是通过默认密钥认证,然后修改密钥,再进行读写数据(修改密钥不是必须的,你可以只进行读写,但是认证是必须的)
https://developers.weixin.qq.com/miniprogram/dev/api/device/nfc/MifareClassic.transceive.html
transceive() 发送数据
*/
nfcMAdapter.transceive({
/*
参数:
1.密钥类型 0x60 是认证密钥A 0x61是认证密钥B 选一个就可以了
2.块 0x07 是指认证的全局块 就是扇区1嘛,前面提到过的
3.卡id 展开上面传递过来的id
4.密钥 0xFF, 0xFF... 是默认密钥,一般空卡都是这个密钥,如果已经加密过的,需要知道修改后的密钥,如果你不知道的话,就没办法读写了
*/
data: new Uint8Array([0x61, 0x07, ...uid, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).buffer,
success(res) {
//默认密钥通过,先进行修改密钥
that.updateSecret(nfcMAdapter)
},
fail(err) {
//默认密钥不通过,则使用修改后的密钥进行认证
that.reAuthSecret(nfcMAdapter,uid)
}
})
}
})
},
//修改密钥
updateSecret(nfcMAdapter) {
//新密钥,看你嵌入式那边咋说了
const newKey = [0x60, 0x68, 0x73, 0x2A, 0x2C, 0x20]
const accessBits = [0xFF, 0x07, 0x80, 0x69];//权限,默认就行
nfcMAdapter.transceive({
/*
参数:
1.指令 0xA0 写入数据 0x30 读取数据
2.块 0x07 扇区1的控制块 修改密钥必须往控制块里面写
3.密钥A
4.权限
5.密钥B
*/
data: new Uint8Array([0xA0, 0x07, ...newKey, ...accessBits, ...newKey]).buffer,
success(res) {
//修改密钥之后,读写数据
that.writeData(nfcMAdapter)
},
fail(err) {
Toast.loading({
message: "密码写入卡片异常\n请再次刷卡",
forbidClick: true,
duration: 0
})
}
})
},
//修改后的密钥进行认证
reAuthSecret(nfcMAdapter, uid) {
//修改后的密钥跟updateSecret方法的新密钥对应
const newKey = [0x60, 0x68, 0x73, 0x2A, 0x2C, 0x20]
nfcMAdapter.transceive({
//认证
data: new Uint8Array([0x61, 0x07, ...uid, ...newKey]).buffer,
success(res) {
//认证成功,读写数据
that.writeData(nfcMAdapter)
},
fail(err) {
Toast.loading({
message: "密码写入卡片异常\n请再次刷卡",
forbidClick: true,
duration: 0
})
}
})
},
//写入数据然后读取
writeData(nfcMAdapter) {
let pwd = []
//遍历你要传递的数据 写入数据必须是16个字节,如果你的数据不够,就在后面加上0xFF或者0x00
for (let i of "123456789") {
pwd.push(i.charCodeAt(0))
}
//假设数据不够长 "123456789"是九位,那就还差7位
let str = [0x00,0x00,0x00,0x00,0x00,0x00,0x00]
pwd.push(...str)
nfcMAdapter.transceive({
/*
写入数据
参数:
1.指令 0xA0 写入数据 0x30 读取数据
2.写入数据的全局块 0x04 因为我们认证的是扇区1的全局控制块,那么可以在全局块4-5-6写入数据
3.数据
*/
data: new Uint8Array([0xA0,0x04,...pwd]).buffer,
success() {
/*
读取数据
参数:
1.指令 0xA0 写入数据 0x30 读取数据
2.读取数据的全局块 0x04 能写就能读
*/
nfcMAdapter.transceive({
data:new Uint8Array([0x30,0x04]).buffer,
success(res){
console.log("读取出来的数据",res);
//断开连接
nfcAdapter.stopDiscovery()
}
})
},
fail() {
Toast.loading({
message: "密码写入卡片异常\n请再次刷卡",
forbidClick: true,
duration: 0
})
}
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
if(nfcAdapter){
nfcAdapter.stopDiscovery()
}
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
可以直接复制代码到JS文件里面尝试一下
验证读写是否成功
打开电脑计算器,里面选择程序员,十进制里面输入49
以此类推,最后得到313233343536373839
拿去转换一下
不知道全局块是0x??
想在扇区13里面写数据,但是不知道该怎么填写。
套用上面的计算公式
13*4+3=55
还是电脑的计算器
结果就是0x37了,0x37是控制块,那么0x34-0x35-0x36就是数据块了