前言
RFID是Radio-Fequency Identification射频识别的缩写。RFID使用电磁场在短距离内传输数据,它可用于人员识别、刷卡交易,商品的电子标签等。工作原理为,ID磁卡进入到磁场后,接受读写器发出的射频信号,凭借感应电流所获得的能量发送出存储在芯片中的产品信息,读写器读取到信息并解码后,送至处理单元进行数据处理。
MFRC522是高度集成的非接触式( 13.56MHz)读写卡芯片。此发送模块利用调制和解调的原理,并将它们完全集成到各种非接触式通信方法和协议中(13.56MHz)。
MFRC522的内部发送器部分可驱动读写器天线与ISO 14443A/MIFARE卡和应答机的通信,无需其它的电路。接收器部分提供一个功能强大和高效的解调和译码电路,用来处理兼容ISO 14443A/MIFARE的卡和应答机的信号。通信距离可达50mm,取决于天线的长度和调谐。数字电路部分处理完整的ISO 14443A帧和错误检测(奇偶&CRC)。
- 支持MIFARE标准器件,如S50、S70,UID卡,
- 支持MIFARE Classic加密。
- 支持MIFARE更高速的非接触式通信,双向数据传输速率高达424kbit/s。
- 内部64字节的发送和接收FIFO缓冲区。
- 10Mbit/s的SPI接口
- I2C接口,快速模式的速率为400kbit/s,高速模式的速率为3400kbit/s
- 串行UART,传输速率高达1228.8kbit/s, 帧取决于RS232接口,电压电平取决于提供的管脚电压
本文将介绍ESP32开发板驱动MFRC522 RFID模块,读取RFID卡原始数据、获取RFID卡的UID,并将个人数据添加到RFID卡中。
ESP32开发板与MFRC522模块接线
下图为SPI通信接线图
ESP32 | MFRC522 |
---|---|
GPIO5 | SDA引脚作为SPI通信时的CS片选 |
GPIO18 | SCK |
GPIO23 | MOSI |
GPIO19 | MISO |
不接 | IRQ |
GND | GND |
GPIO21 | RST |
3V3 | 3.3V |
读取S50 IC白卡与S50 IC钥匙卡扣原始数据
Arduino IDE中安装RFID_MFRCC522驱动库
#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>
// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);
MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{}; // Create I2C driver
MFRC522 mfrc522{driver}; // Create MFRC522 instance
void setup() {
Serial.begin(115200); // Initialize serial communication
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4).
mfrc522.PCD_Init(); // Init MFRC522 board.
MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial); // Show details of PCD - MFRC522 Card Reader details.
Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}
void loop() {
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if (!mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards.
if (!mfrc522.PICC_ReadCardSerial()) {
return;
}
// Dump debug info about the card; PICC_HaltA() is automatically called.
MFRC522Debug::PICC_DumpToSerial(mfrc522, Serial, &(mfrc522.uid));
delay(2000);
}
程序中PICC表示IC卡(proximity integrated circuit cards)
PCD表示读写模块MFRC522(proximity coupling device)
编译上传程序到ESP32开发板 ,打开串口打印,把IC卡靠近MFRC522读写模块,可在串口上打印出IC内存储的数据
从串口打印出的信息可知道
IC卡的存储分布结构划分为16个(0-15)扇区,每个扇区包含4个(0-3)存储块,每个存储块包含16个字节的存储(0-15)
扇区0上的存储块0存储着IC卡的出厂信息,第0-4字节为UUID(如43 17 EE 05),一个校验字节,剩余的为出厂写入的数据。存储块0为只读模式,一般不可更改(部分克隆卡可修改)。
每个扇区的前3个存储块可用于存储数据,每个扇区的最后一个存储块为扇区尾块Sector Trailer。
每个扇区尾块固定为 16字节,其数据结构如下:
0~5 Key A(6字节),扇区的第一个访问密钥(默认通常为 FF FF FF FF FF FF 或厂商预设值)。
6~8 Access Bits(4字节),存储该扇区的访问权限控制位(实际只用了3字节,第9字节为备用)。
10~15 Key B(6字节),扇区的第二个访问密钥(可选,部分应用可能不用或与Key A相同)。
每个扇区的最后一个块是安全核心,决定了该扇区的访问规则。操作时务必谨慎,建议先读取并备份原始数据(需密钥验证),再尝试修改。
IC卡的类型为MIFARE 1KB 用户可用的净存储容量为:
16扇区 X 3存储块 X 16字节 - 16字节=752字节
UID为 43 17 EE 05 不同的卡的UID会不一样
读写用户数据到指定的存储块
#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>
// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);
MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{}; // Create I2C driver
MFRC522 mfrc522{driver}; // Create MFRC522 instance
MFRC522::MIFARE_Key key;
byte blockAddress = 2;
byte newBlockData[17] = {"www.yourcee.com"};
//byte newBlockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // CLEAR DATA
byte bufferblocksize = 18;
byte blockDataRead[18];
void setup() {
Serial.begin(115200); // Initialize serial communication
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4).
mfrc522.PCD_Init(); // Init MFRC522 board.
Serial.println(F("Warning: this example overwrites a block in your card, use with care!"));
// Prepare key - all keys are set to FFFFFFFFFFFF at chip delivery from the factory.
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
}
void loop() {
// Check if a new card is present
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
delay(500);
return;
}
// Display card UID
Serial.print("----------------\nCard UID: ");
MFRC522Debug::PrintUID(Serial, (mfrc522.uid));
Serial.println();
// Authenticate the specified block using KEY_A = 0x60
if (mfrc522.PCD_Authenticate(0x60, blockAddress, &key, &(mfrc522.uid)) != 0) {
Serial.println("Authentication failed.");
return;
}
// Write data to the specified block
if (mfrc522.MIFARE_Write(blockAddress, newBlockData, 16) != 0) {
Serial.println("Write failed.");
} else {
Serial.print("Data written successfully in block: ");
Serial.println(blockAddress);
}
// Authenticate the specified block using KEY_A = 0x60
if (mfrc522.PCD_Authenticate(0x60, blockAddress, &key, &(mfrc522.uid)) != 0) {
Serial.println("Authentication failed.");
return;
}
// Read data from the specified block
if (mfrc522.MIFARE_Read(blockAddress, blockDataRead, &bufferblocksize) != 0) {
Serial.println("Read failed.");
} else {
Serial.println("Read successfully!");
Serial.print("Data in block ");
Serial.print(blockAddress);
Serial.print(": ");
for (byte i = 0; i < 16; i++) {
Serial.print((char)blockDataRead[i]); // Print as character
}
Serial.println();
}
// Halt communication with the card
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
delay(2000); // Delay for readability
}
byte blockAddress = 2;
定义了一个名为blockAddress的变量。这个变量指定了IC卡内将要写入或读取数据的块。blockAddress被设置为2,将与卡片内存的第2个块进行交互。如果你想写入不同的块,你可以更改这个值。
byte newBlockData[17] = {“www.yourcee.com”};
保存您想要写入卡中的数据,不超过16个字节
byte newBlockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
如果您想清除块数据,取消注释这行代码
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
IC卡的默认密钥也在以上行中设置。默认情况下,工厂密钥的所有字节都是0xFF。这个密钥允许访问卡的数据块。
if (mfrc522.PCD_Authenticate(0x60, blockAddress, &key, &(mfrc522.uid)) != 0) {
Serial.println(“Authentication failed.”);
return;
}
代码尝试使用默认密钥(在本例中为块2)对卡片上的特定块进行认证。0x60是一个指定使用KEY_A进行认证的命令。KEY_A是RFID卡上可用的两个密钥(KEY_A和KEY_B)之一,每个密钥提供不同的权限。使用0x60意味着代码正在尝试用KEY_A进行认证,而默认情况下,在MIFARE RFID卡上KEY_A是0xFF 0xFF 0xFF 0xFF 0xFF。
编译上传程序到ESP32开发板 ,打开串口监视器,并把IC卡 靠近MFRC522读写模块,串口显示出写入并读取到指定存储块的数据
总结
本实验只是对空白的MIFARE Classic IC卡进行简单的读写验证,由于IC卡大都有秘钥,至于破解IC卡,复制C卡需要大家进一步探索研究了。