ArduinoでRFIDカードを複製する

힘센캥거루
2025년 11월 26일
5
38

今日はArduinoでRFIDカードを複製する方法について書いてみようと思う。

一度文章にしておけば忘れないので、復習の意味も込めて残しておく。

ArduinoでRFIDカードを複製する-1

1.RFIDカードの内部データ構造

一般的なRFIDカードは MIFARE Classic 1Kカードである。

このカードのメモリ構造は次の通りだ。

- 総容量 1024 bytes (1KB)
- 16個のSector (0~15)
- 各Sectorは4個のBlock (Block 0~3)
- 各Blockは16 bytes

それぞれのセクタは以下のような構造になっている。

Sector n
 ├── Block 0 (Data or UID block)
 ├── Block 1 (Data)
 ├── Block 2 (Data)
 └── Block 3 (Sector Trailer: Key A, Access Bits, Key B)

こうしたセクタの中で最も重要な意味を持つのは、セクタ0の最初のデータだ。

ここにUIDが入っている。

ArduinoでRFIDカードを複製する-2

2.UID

Unique IDentifier(ユニーク・アイデンティファイア)の略で、RFIDカード(例:交通系ICカード、入退室カード、学生証など)ごとに付与された固有識別番号のことを指す。

人に例えると、マイナンバーのような役割だ。

UIDはSector 0のBlock 0内部の先頭4バイトに保存される。

[ UID0 | UID1 | UID2 | UID3 | BCC | Manufacturer Data… ]

RFIDリーダーがカードを読むとき、最初に読み出すのがこの部分である。

これをもとにカードの識別が行われる。

ArduinoでRFIDカードを複製する-3

3. CUIDカード

重要なのは、MIFARE Classic 1K(純正)のUIDは工場で固定されるという点だ。

NXP純正チップのUIDはROMに保存されており、どんな方法でも書き換えることはできない。

したがって、一般的なカードリーダーやMFRC522ではUIDの改ざんは不可能だ。

ArduinoでRFIDカードを複製する-4

しかし市場にはUIDの変更が可能なカードがあり、こうしたカードをCUID(いわゆるマジックカード)と呼ぶ。

先頭のCはChangeable(変更可能)を意味する。

外見はMIFARE Classic 1Kと同じだが、内部チップが次のように異なる。

① Gen1A (UID/Backdoor 対応タイプ)

  • 0x40 / 0x43 バックドアコマンドをサポート

  • UID変更用のコマンドを別途提供

  • MFRC522 + ArduinoライブラリのMIFARE_SetUid()で変更可能

② CUID / Gen2 (Block 0 Writable)

  • バックドアコマンドなし

  • その代わり、Block 0(UIDが入っているセクタ)を通常の書き込み(WRITE)で上書きできるよう設計

  • MFRC522がMIFARE_Write(0…)コマンドを受け付ければUID変更が可能

  • ただし、すべてのCUIDでできるとは限らない。MFRC522が対応していない場合はPN532/ACR122Uが必要

4. ArduinoでRFIDカードを複製する

原理が分かったので、ArduinoでRFIDカードを複製する方法は簡単だ。

UIDをコピーしてやればよい。

まずArduinoを下図のように配線する。

ArduinoでRFIDカードを複製する-5

Signal

MFRC522 Pin

Arduino Uno / 101

Arduino Mega

Arduino Nano v3

Arduino Leonardo / Micro

Arduino Pro Micro

RST / Reset

RST

9

5

D9

RESET / ICSP-5

RST

SPI SS

SDA (SS)

10

53

D10

10

10

SPI MOSI

MOSI

11 / ICSP-4

51

D11

ICSP-4

16

SPI MISO

MISO

12 / ICSP-1

50

D12

ICSP-1

14

SPI SCK

SCK

13 / ICSP-3

52

D13

ICSP-3

15

そしてArduino IDEでMFRC522ライブラリを検索してインストールする。

ArduinoでRFIDカードを複製する-6

次に、[ファイル] -> [スケッチ例] -> [MFRC522] から ReadNUID を選択する。

ArduinoでRFIDカードを複製する-7

そのコードを書き込んだあと、まずUID値を読み取る。

UIDは一般的に16進数4つで構成されている。

F5 5F 36 80

 もし上のようなUIDを取得したなら、書き込むUIDは次のようになる。

数値の前の0xは、その数が16進数であることを意味する。

0xF5 0x5F 0x36 0x80

5. 問題点、そして解決

サンプルにあるChangeUIDで手軽に解決したかったが、何度やっても失敗した。

0x40バックドアが毎回タイムアウトしてしまう。

調べてみると、自分のカードはGen2カードで、単に0番セクタにアクセスしてUIDを入れ替えるだけでよかったのだった。

コードは以下の通り。

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN  9
#define SS_PIN   10

MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;

// 変更したい新しいUID (4バイト)
byte newUid[4] = { 0xF5, 0x5F, 0x36, 0x80 };  // 例

void setup() {
  Serial.begin(9600);
  while (!Serial) {}

  SPI.begin();
  mfrc522.PCD_Init();
  Serial.println(F("CUID カード Block 0 に直接 UID 書き込み例"));

  // デフォルトキー FF..FF 設定
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
}

void loop() {
  // 新しいカード待ち
  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  Serial.print(F("現在のカード UID: "));
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    Serial.print(mfrc522.uid.uidByte[i], HEX);
    Serial.print(" ");
  }
  Serial.println();

  // === 1. セクタ 0 認証 (Block 0) ===
  byte block = 0;  // Block 0
  MFRC522::StatusCode status;

  status = mfrc522.PCD_Authenticate(
      MFRC522::PICC_CMD_MF_AUTH_KEY_A,
      block,
      &key,
      &(mfrc522.uid)
  );

  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("認証失敗: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    goto HALT;
  }

  // === 2. 既存の Block 0 内容を読んで manufacturer 部分を保持 ===
  byte block0[18];  // 16バイト + サイズ情報
  byte size = sizeof(block0);
  status = mfrc522.MIFARE_Read(0, block0, &size);

  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Block 0 読み取り失敗: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    goto HALT;
  }

  // block0[0..3] = 元の UID
  // block0[4]    = BCC (UID 4バイトの XOR)
  // block0[5..15]= メーカー情報など

  // === 3. 新しい UID + BCC を計算して上書き ===
  byte bcc = newUid[0] ^ newUid[1] ^ newUid[2] ^ newUid[3];

  block0[0] = newUid[0];
  block0[1] = newUid[1];
  block0[2] = newUid[2];
  block0[3] = newUid[3];
  block0[4] = bcc;
  // block0[5..15] はそのままにしておけばメーカー情報は維持される

  status = mfrc522.MIFARE_Write(0, block0, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("Block 0 書き込み失敗: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    goto HALT;
  }

  Serial.println(F("新しい UID の書き込み完了。カードを一度離してから再度かざしてください。"));

HALT:
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();

  delay(1000);
} 

6. 感想

今回でRFIDモジュールの正しい使い方をしっかり理解できた。

検索してみるとカードキーでドアを開けるプロジェクトなどがあるが、そういったものも簡単に実装できそうだ。

いろいろと応用してみたい。

ちなみに、この方法でもカードが認識されない場合は、周波数の問題(13.56MHz、125KHz など)の可能性が高い。

もし質問があればコメントしてほしい。

관련 글

micro:bitでクモロボットをコーディングする
micro:bitでクモロボットをコーディングする
今回、ロボット技術関連の集まりで無料講座が一つ開設された。ジェヒョン高等学校で行われた、micro:bit を使ってクモロボットを制御する研修だった。知り合いの先生が申し込んでいたのだが、当日に仕事と重なってしまい、私に席を譲ってくださったので参加してみることにした。サンゲ駅から歩いて10分ほどの距...
Arduino ESP32で温湿度データを収集する
Arduino ESP32で温湿度データを収集する
今日はArduino ESP32で温湿度を測定し、データを送信するWiFi百葉箱を作ってみる。この内容は学校で行われる16+1授業の原稿を基にして書いた文章である。1. 準備物準備物は簡単だ。ESP32、DHT-22、電線3本まずESP32を簡単に説明すると、WiFiモジュールが内蔵されたArduinoである。一般的なArduinoよりもはるかに小さいサイズである。
Arduino D1 R2 使用記
Arduino D1 R2 使用記
ただArduino Unoを使えばよかったのに、内蔵WiFi付きのを試そうとしてWemos D1 R2を使ったせいでかなり苦労しました。この文章はD1 R2のようにピンマッピングが異なるArduinoを使用する人のためのものです。1. IDE設定Arduinoの種類が多様なので、各ボードに合ったボード...
MacBookでArduino Timed out waiting for packet headerの解決方法
MacBookでArduino Timed out waiting for packet headerの解決方法
MacBookでArduino Wemos D1 R2接続時にTimed out問題を解決する方法
Arduinoとスプレッドシートの連携 - コードの構成
Arduinoとスプレッドシートの連携 - コードの構成
前回の記事では、Arduinoとスプレッドシートの連携のためのシート設定について説明しました。今回は、Arduino D1ボードを使ったHTTPS通信によるデータ送信方法について見ていきましょう。1. Arduino D1ボードライブラリのインストールArduino D1ボードを利用するためには、まずボードライブラリをインストールする必要があります。
Arduinoとスプレッドシートの連携 - Googleシート設定
Arduinoとスプレッドシートの連携 - Googleシート設定
最近、生徒たちとArduinoを利用して学校周辺の温度と湿度を観測し、値を分析することにした。Arduinoで測定したデータを保存するにはSDカードが必要で、データを確認するためにはSDカードを抜き差しする手間があった。そこでデ...

댓글을 불러오는 중...