13.56M IC 卡数据读写及密码下载

13.56M IC 卡数据读写及密码下载

本文章参考 FlexLua 官网 ,基于 FlexLua 脱离复杂单片机C开发正逐渐成为一种快速高效的 IoT 硬件开发方式。让小白即使不懂单片机开发也可很容易用FlexLua零门槛开发各种 IoT 硬件。

一、基本概念介绍

一、基本概念介绍

Core通过外接恩智浦半导体公司的RC522 IC卡读卡模块来实现对工作频率为13.56MHz的IC卡的各种操作。RC522支持对Mifare One类型的IC卡操作,例如M1-S20,M1-S50,M1-S70卡等,20,50,70系列的区别主要在存储容量大小,其他的操作基本完全一样。

本篇章主要涉及对IC卡某个扇区(Sector)密码的下载操作,以及对IC卡某个扇区(Sector)中的某个块(Block)进行数据的写入和读出操作。

目前市面上用的最多的IC卡是Mifare One S50 卡,所以Core提供的驱动库完全是按照S50的规格来设计的,但是也兼容S20,S70等卡的读写操作,只是在操作时需注意其容量的差别:

  1. S20卡共有5个扇区(Sector0~Sector4),每个扇区4个块(Block0~Block3),每个块16字节。
  2. S50卡共有16个扇区(Sector0~Sector15),每个扇区4个块(Block0~Block3),每个块16字节
  3. S70卡共有16个扇区(Sector0~Sector15),每个扇区4个块(Block0~Block3),每个块16字节。
  4. 注意:为了和S50保持兼容性,这里只提及了S70的前16个扇区,后面24个扇区不考虑。

S50卡的存储结构如下图:

打开网易新闻 查看精彩图片

二、数据读写操作以及密码下载的相关准则

二、数据读写操作以及密码下载的相关准则

  1. 每个扇区的块0~块2用来读取和写入您自定义的16字节数据。
  2. 每个扇区的块3用来存储该扇区的6字节密码A、4字节的存取控制字节、6字节密码B。一般我们只用密码A作为验证密钥即可。至于块3中的存取控制字节一般保持IC卡出厂默认值0xFF,0x07,0x80,0x69即可,至于块3中的密码B我们需填入一个自定义的6字节数据即可。
  3. 如果要读写操作每个扇区中的块0~块2中的某一块,必须先用密码A认证该扇区。
  4. 实际应用中我们可能只会用到某个扇区中的某一块作为安全认证用,至于剩下的几十个块我们也可以发挥想象来丰富我们的安全认证以及个性化数据存储功能。
三、本例程代码实现功能描述

三、本例程代码实现功能描述

  1. 当有一张新IC卡靠近RC522读卡模块的天线时,如果按下Core电路板上的BTN1按键则将会下载密码到IC卡中指定的扇区中(具体位置是在该扇区的Block3中)。
  2. 当有一张已经下载过密码的IC卡靠近RC522读卡模块的天线时,如果按下Core电路板上的BTN2按键则将会向IC卡中指定的某个扇区(Sector)中的某个块(Block)中写入16字节数据,紧接着继续从该块中回读这16个字节。

本例中指定用来读写数据的块是位于扇区Sector4中的Block1块。当然您也可以从Sector0~Sector15,Block0~Block2中任意选择构成(SectorX, BlockY)组合。数据块的实际地址Addr = SectorX*4 + BlockY, 该Addr开发者无需关心,

四、接线图

四、接线图

打开网易新闻 查看精彩图片

五、材料清单

五、材料清单

  1. RC522读卡模块
六、完整代码

六、完整代码

提示:KEYA一旦下载至某个扇区后,今后对该扇区的块数据的读写前都需要先用这个KEYA验证,如果忘记了KEYA,这张IC卡的这个扇区就无法再次读写了。

--KEYA=0xA0,0xA1,0xA2,0xA3,0xA4,0xA5 KEYB=0xB0,0xB1,0xB2,0xB3,0xB4,0xB5
--KEYA是在本例中我们主要用的密钥,KEYB虽然用不到,但也不能填太容易破解的内容
--0xFF,0x07,0x80,0x69这4个存取控制字节的详细描述文章中已提供参考,如果您有更高开发要求可以前往了解下
DownloadKey = {0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xFF,0x07,0x80,0x69,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5}
KeyA = {0xA0,0xA1,0xA2,0xA3,0xA4,0xA5}
KeyFactory = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} --S50,S70卡等,新出厂时默认的密钥(A或B)全是0xFF,
--需要写入Block(块)中的16字节数据
WriteData = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}
--配置USB以CDC虚拟串口模式工作
LIB_UsbConfig("CDC")
--配置D8为普通输出,控制Core电路板上的LED1
LIB_GpioOutputConfig("D8","STANDARD")
LIB_GpioWrite("D8",1) --LED1灭
--设置Core电路板上的按键1(占用D10口)以低电平有效的方式检测按键动作
LIB_ButtonConfig("BTN1","D10","L")
--设置Core电路板上的按键2(占用D11口)以低电平有效的方式检测按键动作
LIB_ButtonConfig("BTN2","D11","L")
--初始化RC522读卡模块并开始在后台一直寻找有效IC卡的出现
LIB_RC522Config("D7")
--开始大循环
while(GC(1) == true)
do
--如果发现IC卡,就输出其卡类型字符串以及卡的32位UID号
CardFoundFlag,Type,UID = LIB_RC522CardFind()
btn1_value = LIB_ButtonQuery("BTN1")
btn2_value = LIB_ButtonQuery("BTN2")
if CardFoundFlag == 1 then
--如果按键1短按,开始下载密码
if btn1_value == 1 then
--用KeyFactory认证来判断是不是新卡
if LIB_RC522CardAuth("KEYA","Sector4",KeyFactory,UID) == 1 then --新卡
--向Sector4扇区的Block3块中写入16字节的密码及控制数据
--注意:密码必须写入每个扇区的Block3块中才行,这是IC卡厂家已经固定的
if LIB_RC522CardWrite("Sector4", "Block3", DownloadKey) == 1 then --写密码成功
print("KEY download success\r\n")
else--写操作失败
print("KEY download fail\r\n")
end
else
print("You can not download key to an used card\r\n")
end
--如果按键2短按,开始写操作并回读刚才写的内容
elseif btn2_value == 1 then
--开始写操作
--用KeyA认证来判断是不是有效的卡
if LIB_RC522CardAuth("KEYA","Sector4",KeyA,UID) == 1 then --新卡
--向Sector4扇区的Block1数据块中写入16字节的数据
WriteData[1] = WriteData[1] + 1 --每次写入时第1个字节加1
if LIB_RC522CardWrite("Sector4", "Block1", WriteData) == 1 then --写密码成功
print("Write success\r\n")
else--写操作失败
print("Write fail\r\n")
end
else
print("You can not write to an invalid card\r\n")
end
--开始回读操作
--用KeyA认证来判断是不是有效的卡
if LIB_RC522CardAuth("KEYA","Sector4",KeyA,UID) == 1 then --新卡
--读取刚刚写操作所涉及的Sector4扇区的Block1块
result,rd = LIB_RC522CardRead("Sector4", "Block1")
if result == 1 then --读操作成功
print("Sector4 Block1 Content:\r\n")
--这里只打印了前2个字节和最后2个字节
print(string.format("%02x%02x...%02x%02x",rd[1],rd[2],rd[15],rd[16]))
else
print("Read fail\r\n")
end
else
print("You can not read an invalid card\r\n")
end
--如果没有按键动作,就只打印出卡的类型和32位UID
else
--LED1闪烁一下
LIB_GpioWrite("D8",0)
LIB_DelayMs(200)
LIB_GpioWrite("D8",1)
end
end
end

如果感兴趣,上面代码中出现的LIB开头的库函数可以在 API文档 中通过Ctrl+F查询。

七、代码运行结果

七、代码运行结果

在电脑端运行的调试助手上,我们可以看到如下打印输出信息:

打开网易新闻 查看精彩图片

附录:S50 卡存储控制字节详细

附录:S50 卡存储控制字节详细

一、S50卡每个扇区中的块3中的4个字节的存取控制字介绍:

一、S50卡每个扇区中的块3中的4个字节的存取控制字介绍:

存取控制字的组合方式虽然多,但常用的有如下四种:

  1. 出厂默认方式 控制位为“FF 07 80 69”
    这种方式下密钥A或密钥B都可以读写数据区,密钥A可写密钥区,优点是密钥控制字无需重新计算,读写方便,缺点是安全性能差,密钥A容易泄露。
  2. 密钥B写方式 控制位为“7F 07 88 69”
    这种方式下密钥A或密钥B都可以读写数据区,而对于密钥区只能由密钥B来写。优点是密钥B权限最高,只要知道密钥B,无论密钥A写成什么都可以改写,由最高管理员掌握密钥B,可下发多种密钥A的一般管理员,一般不会废卡的。缺点是密钥B很重要,一旦忘记,卡就不能再改写密钥了。
  3. A读B写方式 控制位为“08 77 8F 69”
    这种方式下由密钥A读密钥B来写,可以说是上面一种方式的变体,对于密钥B有更强的保护。
  4. 只读不写方式 控制位为“FF 00 F0 69”
    这种方式下密钥A或密钥B都可以读数据区,但都不能写数据区(数值可减少,不能增加),密钥A可以改写密钥区。这种方式对于数据是极大的保护,尤其是定额卡,里面的钱只能减少而不能增加。

利用 M1卡控制字节生成工具.exe 工具软件我们可以更直观的设计出我们想要的存取控制方式。