微信小程序NFC实战:从零到一读写M1卡并自定义密钥
1. 微信小程序NFC开发入门指南第一次接触微信小程序的NFC功能时我也是一头雾水。记得去年公司要做一个会议签到系统领导说要用NFC卡我当时心里直打鼓这玩意儿能行吗但实际做下来发现微信小程序的NFC API其实比想象中友好得多。M1卡Mifare Classic是目前最常见的NFC卡片之一它的存储结构就像一本有16个章节的书。每个章节扇区有4页块其中最后一页记录着这个章节的访问密码和权限设置。这种结构设计让M1卡既灵活又安全特别适合门禁、会员卡这类场景。要开发NFC功能首先得确认两件事一是手机支持NFC现在中高端机型基本都支持二是微信版本要够新。我遇到过最坑的情况是测试时一切正常上线后用户反馈用不了最后发现是用户手机系统禁用了NFC权限。2. M1卡结构深度解析2.1 扇区与块的秘密M1卡的存储结构特别有意思。它把数据分成16个扇区编号0-15每个扇区又包含4个块编号0-3。这就像一栋16层的公寓每层有4个房间。但要注意的是0层的0号房间扇区0块0是开发商专属的里面存着卡片ID等固定信息我们普通用户改不了。其他楼层的3号房间块3最特别它相当于这层的物业办公室里面放着两把钥匙密钥A和密钥B和一张门禁规则表存取控制。我刚开始做的时候经常搞混块地址的计算方法后来发现用这个公式就很简单全局块号 扇区号 × 4 块号比如要访问第5扇区的第2块就是5×4222对应的十六进制是0x16。这个转换用Windows自带的计算器就能搞定切换到程序员模式十进制输入22十六进制显示就是0x16。2.2 控制块的玄机块3控制块里有6个字节的密钥A、4个字节的存取控制、6个字节的密钥B。存取控制这4个字节决定了谁能用什么密钥访问哪些块。新手最容易踩的坑就是乱改存取控制结果把卡片锁死。我建议先用默认值FF078069等熟悉了再调整。这里有个实用技巧用NFC Tools这类APP可以直观查看卡片结构。我调试时就经常用它先扫描卡片确认各个扇区状态比盲目操作高效多了。3. 微信小程序NFC开发全流程3.1 初始化与发现卡片微信小程序的NFC API设计得很简洁。首先获取NFC适配器实例这个相当于拿到了NFC功能的遥控器nfcAdapter wx.getNFCAdapter()然后启动发现模式就像打开雷达搜索附近的飞机nfcAdapter.startDiscovery({ success(res) { // 准备就绪等待刷卡 }, fail(err) { // 处理错误 } })这里有个细节要注意一定要在fail回调里处理13000设备不支持和13001NFC未开启这两个错误码。我见过不少开发者只写success逻辑结果用户手机不支持NFC时小程序就卡死了。3.2 认证与连接检测到卡片后要先用密钥敲门。M1卡出厂时所有扇区都用默认密钥FFFFFF6个0xFF但为了安全建议尽快修改。认证流程是这样的nfcMAdapter.transceive({ data: new Uint8Array([0x61, 0x07, ...uid, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]).buffer, success(res) { // 认证成功 }, fail(err) { // 认证失败 } })这里的0x61表示用密钥B认证用密钥A则是0x600x07是目标块号第1扇区的控制块后面跟着卡片UID和密钥。我第一次写的时候漏掉了UID调试了半天才发现问题。4. 密钥修改与数据读写实战4.1 自定义密钥的正确姿势修改密钥不是简单的覆盖写入而是要同时更新密钥和存取控制。这里有个安全建议密钥A和密钥B最好设成不同的值比如用密钥A只读密钥B可读写。修改密钥的代码长这样const newKeyA [0x60, 0x68, 0x73, 0x2A, 0x2C, 0x20]; const newKeyB [0x20, 0x2C, 0x2A, 0x73, 0x68, 0x60]; const accessBits [0xFF, 0x07, 0x80, 0x69]; nfcMAdapter.transceive({ data: new Uint8Array([0xA0, 0x07, ...newKeyA, ...accessBits, ...newKeyB]).buffer, success(res) { // 修改成功 } })特别提醒修改前一定要备份原密钥我有次现场演示时手快把测试卡锁死了场面一度十分尴尬。4.2 数据读写技巧写入数据时要注意两点一是数据长度必须是16字节不足要补0x00或0xFF二是最好先擦除再写入。读取数据相对简单// 写入数据 nfcMAdapter.transceive({ data: new Uint8Array([0xA0, 0x04, ...data]).buffer }) // 读取数据 nfcMAdapter.transceive({ data: new Uint8Array([0x30, 0x04]).buffer, success(res) { console.log(res.data) // 读取到的数据 } })实际项目中我建议在数据块开头加个魔数比如0x5A5A这样读取时能快速判断数据是否有效。另外小程序端最好对读写操作做队列管理避免快速连续操作导致失败。5. 常见问题排查指南调试NFC功能时我总结了个三步走策略一看权限二看连接三看数据。首先确认手机NFC已开启且小程序有权限然后检查卡片是否成功连接最后验证数据格式是否正确。有个特别实用的调试技巧用微信开发者工具的真机调试功能配合console.log输出完整交互流程。我遇到过一个诡异问题开发时一切正常上线后部分用户反馈读不到数据。最后发现是某些手机厂商的省电模式限制了NFC功能需要在系统设置里把微信加入白名单。对于数据解析问题推荐使用在线Hex工具。比如读取到数据是[0x31,0x32,0x33]直接复制到在线转换工具就能看到对应的ASCII码是123。这种小技巧能节省大量调试时间。