最近工作需要购买了一些nfc卡进行读写操作,目标是使用手机实现一些业务流程
淘宝购买1-2块钱一张卡,卖家提供的信息:卡的芯片是215
网上找的内容都是一段一段的,我现在把用到的资料都整合起来,以便看一篇就可以知道怎么操作。
安卓支持NFC手机,都比较容易的使用,比较舒服
苹果手机有点特别,限制比较多,没有安卓那么开放,NFC卡使用说明如下:
苹果手机读取,需要iphone7+ ios12以上,才有nfc功能,读取时不能直接读取卡序列号,需要用安卓手机安装NFC TagWriter by NXP_v4.8.2_apkpure.com.apk (Google play可以下载)进行写卡后,苹果才可以读取写入的内容
一、该卡属于MifareUltralight格式规范,规范如下:
https://blog.csdn.net/wxh0000mm/article/details/79708807?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
这里有两个地方特别独家补充一下的,网上的网文都没有说明清楚,看得到有点晕
1、是卡的序列号使用16进制读取
2、page2中lock使用二进制来标记(再转16进制)设置
存储结构:
页号 | Byte0 | Byte1 | Byte2 | Byte3 | 说明 |
0 | SN0 | SN1 | SN2 | BCC0 | 只读,存放卡的序列号:Page0前3字节+整个Page1 这里是16进制读取(注意读取方式,其它网文都没有标明) |
1 | SN3 | SN4 | SN5 | SN6 | |
2 | BCC1 | 保留 | LOCK0 | LOCK1 | 只读,通过设置LOCK0和LOCK1可以讲16个page设为只读 二进制方式设置(注意读取方式,其它网文都没有标明) |
3 | OTP0 | OTP1 | OTP2 | OTP3 | 可读写,一次性交易计数器,不可逆 |
4 | Data0 | Data1 | Data2 | Data3 | 可读写,数据存放区域 |
5 | Data0 | Data1 | Data2 | Data3 | |
6 | Data0 | Data1 | Data2 | Data3 | |
7 | Data0 | Data1 | Data2 | Data3 | |
8 | Data0 | Data1 | Data2 | Data3 | |
9 | Data0 | Data1 | Data2 | Data3 | |
10 | Data0 | Data1 | Data2 | Data3 | |
11 | Data0 | Data1 | Data2 | Data3 | |
12 | Data0 | Data1 | Data2 | Data3 | |
13 | Data0 | Data1 | Data2 | Data3 | |
14 | Data0 | Data1 | Data2 | Data3 | |
15 | Data0 | Data1 | Data2 | Data3 |
Page2的第3和第4个字节用于将存储区锁定为只读,如下图示,L4-L15的某一位设置为1,则对应序号的Page内容锁定为只读,每一个Page都可以单独设置。Lotp用于锁定Page3为只读。Lotp-L15可以锁定别人,这些位本身又被三个BL位锁定,BL15-10用于锁定L15-L10,BL9-4用于锁定L9-L4,BLotp用于锁定Lotp。所有的这16个锁定位也具有OTP特性,通俗的讲就是这些“锁”没有“钥匙”,一旦锁死就改不回来,所以锁定时一定要小心。
如lock1对应8位二进制11110000,1代表锁定只读,然后把二进制转换成16进制写入卡
二进制方式设置(注意读取方式,其它网文都没有标明)
二、读写方法网上有比较多,写法如下:
https://www.cnblogs.com/sjjg/p/4783743.html
亲测有效,我的测试源代码下载地址:https://download.csdn.net/download/qq_16005627/12366636
通过以上读写方法扩展
1、 读取卡序列号方法
public String readTagc(Tag tag) throws Exception {//读数据 第1步,从nfc标签中得到MifareUltralightMifareUltralight ultralight = MifareUltralight.get(tag);try {//读数据 第2步,接连ultralight.connect();//读数据 第3步,从ultralight数据中的下标为4的位开始读数据.byte[] data = ultralight.readPages(0);byte[] serialNumber = new byte[7];serialNumber[0] = data[0];serialNumber[1] = data[1];serialNumber[2] = data[2];serialNumber[3] = data[4];serialNumber[4] = data[5];serialNumber[5] = data[6];serialNumber[6] = data[7];String CardCode = bytes2HexString(serialNumber); return CardCode ;} catch (Exception e) {e.printStackTrace();} finally {try {ultralight.close();} catch (Exception e) {e.printStackTrace();throw e;}}return null;
}
//16进制转字符串
private static String bytes2HexString(final byte[] bytes) {if (bytes == null) return "";int len = bytes.length;if (len <= 0) return "";char[] ret = new char[len << 1];for (int i = 0, j = 0; i < len; i++) {ret[j++] = HEX_DIGITS[bytes[i] >> 4 & 0x0f];ret[j++] = HEX_DIGITS[bytes[i] & 0x0f];}return new String(ret);
}private static final char[] HEX_DIGITS ={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
二、设置page2中lock
ultralight.writePage(2, hexString2Bytes("DF48f000"));//第2页 LOCK设置 11110000二进制转16进制
//16进制转字节
public static byte[] hexString2Bytes(String src) int l = src.length() / 2;byte[] ret = new byte[l];for (int i = 0; i < l; i++) {ret[i] = (byte) Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();}return ret;
}
觉得好的同学,记得点个赞!