最近工作需要购买了一些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标签中得到MifareUltralight
MifareUltralight 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;
}
觉得好的同学,记得点个赞!