读取 Mifare Classic returns 个奇怪的字符

Reading Mifare Classic returns strange characters

当使用 Android 读取 MIFARE 卡并将数据转换为 UTF-8 时,我得到奇怪的字符,例如 �。 我正在尝试构建一个可以读取我们正在使用的某种身份证的应用程序。现在的问题是我在单词之间得到奇怪的字符,并且一些单词在块之间分开,所以我怎样才能安全地得到我正在寻找的单词? 例如我的读数是这样的:

43224����19032019�� 在 block 2 sektor 2 bindex :8

并进行拆分,其中以 19 开头的其余数字位于新块中:

我的名字��M����19

在 block 1 sektor 1 bindex :4

930402����NO934951

在 block 2 sektor 1 bindex :4

c5 42 4e 49 44 00 07 4f 4f 4f 4f 4f 4f 00 4b 42   "Åbnid" "OOOOOO" "KB"
44 44 44 20 44 44 44 44 44 00 82 4d 00 c9 31 39   "DDD DDDDD" "M" "19"
39 34 34 33 34 32 00 d0 4e 4f 39 36 36 36 35 31   "944342" "NO966651"
00 00 00 00 00 00 70 f7 88 00 00 00 00 00 00 00
30 32 32 20 20 41 53 00 d3 54 4f 54 41 4c 20 4b   "022" "AS" "Total k"
4f 4e 54 52 4f 4c 4c 20 41 53 20 00 c9 30 32 38   "ONTROLL AS" "028"
37 30 34 33 33 00 c9 32 30 32 31 30 32 31 31 00   "70433" "20210211"
00 00 00 00 00 00 70 f7 88 00 00 00 00 00 00 00

我是这样读卡片的:

Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
MifareClassic mfc = MifareClassic.get(tagFromIntent);

这是我用于在 for 循环中读取的代码:

 data = mfc.readBlock(bIndex + block); 

然后为了将数据转换为 UTF8 以进行打印,我使用:

   public String convertByteArrayToUTF8(byte[] bytes){
    String encoded = null;
    try {
        encoded = new String(bytes, StandardCharsets.UTF_8);
    }
    catch (Exception e){
        encoded = new String(bytes, Charset.defaultCharset());
    }
    return encoded;
}

我尝试过使用 ASCII、UTF-16 等,但没有成功。

首先对问题标题大声笑。当我是新手时,我处于同样的情况。没有在线教程可以为您提供从 Mifare 经典卡读取数据的确切代码。

先了解一下Mifare卡的内存结构

Mifare Classic的内存分为扇区,扇区也分为16字节的块。

MIFARE Classic 1K卡有16个扇区,每个扇区分为四个块。如果我们进行数学计算,我们可以算出内存结构是怎样的:16 字节(1 块)* 4 块 * 16 扇区 = 1024 字节。

MIFARE Classic 4K卡有40个扇区,其中32个扇区分为4个块,其余8个扇区分为16个块。 16字节(1块)*4块*32扇区+16字节(1块)*16块*8扇区=4096字节。内存结构如下:

方块上的数字表示其索引。每个扇区都由写在扇区最后一个块中的站点密钥保护。例如,块 3 包含扇区 1 的站点密钥,块 7 包含扇区 2 的站点密钥。每个扇区中的最后一个块还包含访问条件信息,例如“写入”、“读取”和“读写”。下图演示了最后一个块是如何组成的:

此外,写入卡中的数据是二进制的,即; 0 和 1。

现在,读取数据需要遵循的步骤是:

第一步:检查设备是否支持NFC

step2:检查设备是否有NXP芯片(尤其是读取Mifare classic卡)

step3: 实例化NFC manager和NFC adpater & 定义要读取的卡的技术列表。

第 4 步:请求访问设备 NFC 的权限。

step5:创建一个检测卡的intent,指定要读取的MIME类型(大部分情况下都是MIME类型)。

step6: 在 onResume() 和 onPause() 中启用和禁用适配器的前台调度,以便您的应用程序在您的 activity 处于前台时优先读取卡片。

step7:当卡片接触到设备时,可以从intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

中获取标签信息

step8:读取卡片信息即;卡片类型,技术列表等..

step9:要读取卡片中的数据,需要通过上面检索到的标签信息连接到卡片。

step10:遍历所有扇区。使用默认密钥验证每个扇区 //https://developer.android.com/reference/android/nfc/tech/MifareClassic.html#authenticateSectorWithKeyA(int,%20byte[])

step11:认证成功后读取每个扇区块中的二进制数据。

step12:将二进制数据转换为字符串数据,以便我们读取。

step13:就这些了,想怎么处理数据就怎么处理吧

惊喜!在我的 github 存储库中获取完整的工作代码: https://github.com/codes29/RFIDReader

注意:我理解你作为新手的感受,并在没有适当教程的情况下完成了这项任务。所以我更新了我经过几天的努力后写的代码。

这是您在成功验证并读取数据后将获得的样本。我扫描的卡现在是空的。但是如果这里有数据那么它肯定会在这里而不是0。

干杯!兄弟,编码愉快!

所以你标签上的数据(不包括扇区尾部看起来有点像这样:

C5 42 4E 49 44 00 07 4F 4F 4F 4F 4F 4F 00 4B 42        ÅBNID..OOOOOO.KB
44 44 44 20 44 44 44 44 44 00 82 4D 00 C9 31 39        DDD DDDDD.‚M.É19
39 34 34 33 34 32 00 D0 4E 4F 39 36 36 36 35 31        944342.ÐNO966651
30 32 32 20 20 41 53 00 D3 54 4F 54 41 4C 20 4B        022  AS.ÓTOTAL K
4F 4E 54 52 4F 4C 4C 20 41 53 20 00 C9 30 32 38        ONTROLL AS .É028
37 30 34 33 33 00 C9 32 30 32 31 30 32 31 31 00        70433.É20210211.

这似乎是某种形式的结构化数据。简单地将整个二进制 blob 转换为 UTF-8(或 ASCII)编码的字符串没有多大意义。相反,您将需要对数据的结构方式进行逆向工程(或者,更好的是,您尝试从系统制造商处获取规范)。

据我所见,该数据似乎由嵌入某种紧凑(标记)-长度-值格式的多个以 null 结尾的字符串组成。第一个字节似乎是标签(?)+长度,所以我们有

C5    Length = 5
    42 4E 49 44 00                                               "BNID"
07    Length = 7
    4F 4F 4F 4F 4F 4F 00                                         "OOOOOO"
4B    Length = 11
    42 44 44 44 20 44 44 44 44 44 00                             "KBDDD DDDDD"
82    Length = 2
    4D 00                                                        "M"
C9    Length = 9
    31 39 39 34 34 33 34 32 00                                   "19944342"
D0    Length = 16
    4E 4F 39 36 36 36 35 31 30 32 32 20 20 41 53 00              "NO966651022  AS"
D3    Length = 19
    54 4F 54 41 4C 20 4B 4F 4E 54 52 4F 4C 4C 20 41 53 20 00     "TOTAL KONTROLL AS "
C9    Length = 9
    30 32 38 37 30 34 33 33 00                                   "02870433"
C9    Length = 9
    32 30 32 31 30 32 31 31 00                                   "20210211"

例如,第一个字节可以分为标签和长度,如下所示:TTTL LLLL(高 3 位编码标签,低 5 位编码以下值的长度)。这将给出以下标签

  • 0x6 "BNID"、“19944342”、"NO966651022 AS"、"TOTAL KONTROLL AS "、“02870433”和“20210211”
  • 0x0 对于 "OOOOOO"
  • 0x2 对于 "KBDDD DDDDD"
  • 0x4 对于 "M"

因此,标签和长度之间的拆分也可能是 TTLL ​​LLLL(高 2 位编码标签,低 6 位编码以下值的长度)。

不幸的是,该格式与我所知道的任何流行格式都不相似。因此,您可以通过比较多张不同的卡片并从值中获取含义来继续您的逆向工程。

到目前为止,为了解码上述内容,您将从读取第一个字节开始,提取该字节的长度,削减后续字节的数量并将它们转换为字符串(基于示例您提供的 ASCII 编码应该可以)。然后您可以继续下一个字节,从中提取长度信息,...