当使用Android读取MIFARE卡并将数据转换为UTF-8时,会出现类似�的奇怪字符。 我正在尝试构建一个可以读取我们正在使用的身份证的应用程序。现在的问题是,我在单词之间出现奇怪的字符,并且某些单词在块之间分割,因此如何安全地找到要查找的单词? 例如我的读物是这样的:
43224���19032019�� 在块2 sektor 2 bindex:8
并进行拆分,其中以19开头的数字的其余部分位于一个新的块上:
我的名字�M���19
在第1区sektor 1 bindex:4
930402.��NO934951
在第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等运气不好。
答案 0 :(得分:5)
首先要对问题标题进行大声笑。当我是新手时,我处在同样的情况。没有在线教程可以为您提供从Mifare经典卡读取数据的确切代码。
首先了解Mifare卡的内存结构。
Mifare Classic的内存分为多个扇区,这些扇区也分为16个字节的块。
MIFARE Classic 1K卡有16个扇区,每个扇区分为四个块。如果进行数学计算,我们可以算出存储器的结构:16字节(1块)* 4块* 16扇区= 1024字节。
MIFARE Classic 4K卡有40个扇区,其中32个分为四个块,其余8个分为16个块。 16字节(1块)* 4块* 32扇区+ 16字节(1块)* 16块* 8扇区= 4096字节内存结构如下:
块上的数字表示其索引。每个扇区均受该扇区最后一块中写入的站点密钥的保护。例如,块3包含扇区1的站点密钥,而块7包含扇区2的站点密钥。每个扇区中的最后一个块还包含访问条件信息,例如“写入”,“读取”和“读取和写入”。下图演示了最后一个块是如何组成的:
此外,写入卡中的数据是二进制的,即; 0&1.
现在,您需要按照以下步骤读取数据:
第一步:检查设备是否支持NFC。
步骤2:检查设备是否具有NXP芯片(特别是用于读取Mifare经典卡)。
步骤3:实例化NFC管理器和NFC adpater并定义要读取的卡的技术列表。
步骤4:要求访问设备NFC的权限。
第5步:创建一种意图来检测卡并指定要读取的MIME类型(在大多数情况下,所有的MIME类型)。
第6步:在onResume()和onPause()中启用和禁用适配器的前台分派,以便您的应用在活动处于前台时优先读取卡。
步骤7:当卡片与设备联系时,您可以从intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
中获取标签信息
步骤8:读取卡信息,即卡类型,技术列表等。
步骤9:要读取卡中的数据,您需要通过上面获取的标签信息连接到卡。
步骤10:遍历所有扇区。使用默认密钥对每个扇区进行身份验证 // https://developer.android.com/reference/android/nfc/tech/MifareClassic.html#authenticateSectorWithKeyA(int,%20byte[])
步骤11:在成功进行身份验证后,读取每个扇区块中的二进制数据。
step12:将二进制数据转换为字符串数据,以便我们可以读取它。
step13:仅此而已,就可以对数据做任何想做的事情。
惊奇!在我的github仓库中获得完整的工作代码: https://github.com/codes29/RFIDReader
注意:我很体谅您作为新手的感觉,并在没有合适的教程的情况下完成了此任务。经过几天的努力,我更新了自己编写的代码。
这是成功进行身份验证并读取数据后将获得的示例。我扫描的卡现在是空的。但是,如果这里有数据,那么肯定会在这里而不是0。
干杯!兄弟,祝您编码愉快!
答案 1 :(得分:2)
因此,标签上的数据(不包括扇区尾部)看起来像这样:
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)编码的字符串没有多大意义。相反,您将需要对数据的结构方式进行逆向工程(或者,甚至更好的是,您尝试从系统制造商那里获取规范)。
据我所见,数据看起来好像是由多个以空值结尾的字符串组成,这些字符串以某种紧凑的(Tag)-Length-Value格式嵌入。第一个字节似乎是tag(?)+长度,所以我们有
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位编码后续值的长度)。这将给出以下标签
_o.onWheel @ mapbox-gl.js:33
i.addEventListener.passive @ mapbox-gl.js:33
push.../../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
push.../../../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
push.../../../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:496
invokeTask @ zone.js:1540
globalZoneAwareCallback
代表“ BNID”,“ 19944342”,“ NO966651022 AS”,“ TOTAL KONTROLL AS”,“ 02870433”和“ 20210211” yo._onMove @ mapbox-gl.js:33
push.../../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
push.../../../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
push.../../../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:496
invokeTask @ zone.js:1540
globalZoneAwareCaptureCallback
代表“ OOOOOO” 0x6
代表“ KBDDD DDDDD” 0x0
代表“ M” 因此,标记和长度之间的距离也可能是TTLL LLLL(高2位编码标记,低6位编码以下值的长度)。
不幸的是,该格式与我所知道的任何流行格式都不相似。因此,您可以通过比较多个不同的卡并从值中得出含义来继续进行逆向工程。
到目前为止,为了解码上述内容,您将从读取第一个字节开始,从该字节中提取长度,减少后续字节的数量,然后将它们转换为字符串(基于您所采样的提供,则应使用ASCII编码)。然后,您可以继续下一个字节,从中提取长度信息,...