Mifare Ultra Light 非接触式IC卡发卡总结
概述:
1、 容量512bit,分为16个page,每个page占4byte
2、 每个page可以通过编程的方式锁定为只读功能
3、 384位(从page4往后)用户读写区域
4、 唯一7字节物理卡号(page0前3个byte加page1)
存储结构:
页号 |
Byte0 |
Byte1 |
Byte2 |
Byte3 |
说明 |
0 |
SN0 |
SN1 |
SN2 |
BCC0 |
只读,存放卡的序列号:Page0前3字节+整个Page1 |
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 |
总的来说,你可以把UltraLight卡简单地看成是一种存储介质,对它的操作也就是对扇区中每个Page的读取和写入的过程。
UltraLight卡的发卡流程:
第一步:寻卡
dc_card(icdev, cardMode, snr)
dc_anticoll2(icdev, 0, snr2)
dc_select2(icdev, snr2, size)
第二步:写卡
获得想要写入卡片的数据(十六进制字符串形式),调用dc_write_hex函数,写入对应page
关于这个函数有个比较怪的问题,文档中规定,对于Ultralight卡,一次必须写入4个字节,也就是一个Page,但你仍需将你写入的数据以后补0的方式补足16字节,尽管它实际写入的只是前4个字节。自己写的功能函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | '' ' <summary> '' ' 将进制串写入指定地址的卡片内存中 '' ' </summary> '' ' <param name= "start" >要写入的起始块地址</param> '' ' <param name= "finish" >要写入的结束块地址</param> '' ' <param name= "dataBufHex" >要写入的串</param> '' ' <returns>写卡是否成功</returns> '' ' <remarks></remarks> Private Function Write(ByVal start As Short, ByVal finish As Short, ByVal dataBufHex As String) Dim i As Byte '循环标记 Write = False '假设dataBufHex为字8节,start为4,finsh为5,但对Ultralight来说,一次只能写4个字节,即 '一次只能写一个块,因此需要分两次来写。虽然每次只能写四个字节,但 'dc_write_hex函数规定写入的数据必须为16字节,因此需要做右补0操作 For i = 0 To (finish - start) Dim tmp As String tmp = Mid(dataBufHex, i * 8 + 1, 8) '补0 If Len(tmp) < 32 Then tmp = tmp & New String( "0" , 32 - Len(tmp)) End If Log( "往Page" & (start + i) & "中写数据:" & tmp) st = dc_write_hex(icdev, start + i, tmp) If (st <> 0) Then '写卡失败 Write = False Exit Function End If Next Write = True End Function |
第三步:读卡
发好卡后,需要验证写入的数据是否正确,这时你就需要调用dc_read_hex函数读入卡片中的数据,有例子:
st = dc_read_hex(icdev, address, dataBufHex)
这里icdev是通讯设备标识符,address是要读入的起始页地址,dataBufHex是存放读入数据的变量。
dc_read_hex函数一次性读入16字节数据,因此dataBufHex字符串长度最长为32,但是不管你的dataBufHex设成任何长度(只要不大于32),该函数仍然读出16字节数据,只不过它会截取前面的部分放到dataBufHex中返还给你罢了。
其他:
关于发行验证码和交易TAC的计算方法,等过几天总结了CPU卡和M1卡的发卡流程之后再写出来。还有就是卡片的应用,我发的UltraLight是拿来作为单程票的,它的消费充值等流程是由其他人来做的,而且跟具体的卡结构有关,不好讲。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库