最近工作需要購買了一些nfc卡進行讀寫操作,目標是使用手機實現一些業務流程
淘寶購買1-2塊錢一張卡,賣家提供的信息:卡的芯片是215
網上找的內容都是一段一段的,我現在把用到的資料都整合起來,以便看一篇就可以知道怎麼操作。
蘋果手機的NFC卡使用說明:
蘋果手機讀取,需要iphone7+ ios12以上,纔有nfc功能,讀取時不能直接讀取卡序列號,安卓可以,需要用安卓手機安裝NFC TagWriter by NXP_v4.8.2_apkpure.com.apk (Google play可以下載)進行寫卡後,蘋果纔可以讀取寫入的內容
一、該卡屬於MifareUltralight格式規範,規範如下:
這裏有兩個地方特別獨家補充一下的,網上的網文都沒有說明清楚,看得到有點暈
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;
}
覺得好的同學,記得點個贊!