在使用 Mifare Classic 工具读取标签之前,某些设备上的 Mifare Classic 身份验证失败

Mifare Classic Authentication failure on some devices until the tag has been read with the Mifare Classic Tool

我正在尝试从 Xamarin Forms Android 应用中的 Mifare Classic 卡的扇区 0 块 1 读取用户数据。在大多数设备上,它每次都能完美运行,但在 Unitech EA510 上,身份验证始终失败,除非我首先使用 Mifare Classic Tool (https://play.google.com/store/apps/details?id=de.syss.MifareClassicTool) 读取卡片。一旦我使用 Mifare Classic 工具检测到任何标签,我的代码每次都能完美运行。我已经检查了 Mifare Classic Tool 源代码以查看它还做了什么并借用了 patchTag 逻辑(它没有帮助!)。

  1. 第一次使用应用程序扫描标签: Mifare Classic标签认证失败:8250A9B9
  2. 使用 Mifare Classic 工具扫描标签
  3. 用应用程序重新扫描标签: 找到 Mifare Classic 标签:8250A9B9 10240472
  4. 用应用扫描另一个标签: 找到 Mifare Classic 标签:8250A9C7 10395802
public void OnTagDiscovered(Android.Nfc.Tag tag)
{
    string tagId = string.Empty;
    string[] techList = tag.GetTechList();
    string sUid = string.Empty;
    if (Array.IndexOf(techList, "android.nfc.tech.MifareClassic") > -1) {
        MifareClassic mfc = null;
        try {
            sUid = BitConverter.ToString(tag.GetId()).Replace("-", "");
            mfc = MifareClassic.Get(tag);
            mfc.Connect();
            bool bAuthenticated = false;
            if (mfc.AuthenticateSectorWithKeyA(0, MifareClassic.KeyDefault.ToArray()))
                bAuthenticated = true;
            else if (mfc.AuthenticateSectorWithKeyB(0, MifareClassic.KeyDefault.ToArray()))
                bAuthenticated = true;
            else if (mfc.AuthenticateSectorWithKeyA(0, MifareClassic.KeyMifareApplicationDirectory.ToArray()))
                bAuthenticated = true;
            else if (mfc.AuthenticateSectorWithKeyB(0, MifareClassic.KeyMifareApplicationDirectory.ToArray()))
                bAuthenticated = true;
            else if (mfc.AuthenticateSectorWithKeyA(0, MifareClassic.KeyNfcForum.ToArray()))
                bAuthenticated = true;
            else if (mfc.AuthenticateSectorWithKeyB(0, MifareClassic.KeyNfcForum.ToArray()))
                bAuthenticated = true;
            if (bAuthenticated) {
                int blockOffset = mfc.SectorToBlock(0);
                byte[] block = mfc.ReadBlock(blockOffset + 1); // Sector 0/block 0 is the UID so go straight to block 1 for the payload
                tagId = System.Text.Encoding.ASCII.GetString(block);
                if (tagId.Length == 1 && tagId[0] == 0x04)
                    // Ignore if ReadBlock returns a single byte 0x04, which appears to happen if there's a delay between the tag discovery and ReadBlock (e.g. running in the debugger) and we don't want to treat it as a conventional tag by acciden
                    tagId = string.Empty;
                else if (tagId.Length > 0 && tagId.Contains('[=10=]')) 
                    tagId = tagId.Substring(0, tagId.IndexOf('[=10=]'));
            } else {
                Console.WriteLine($"Mifare Classic tag authentication failure: {sUid}");
            }
        }
        catch (Exception ex) {
            Console.WriteLine($"Mifare Classic tag read exception: {ex.Message}");
        }
        finally {
            if (mfc != null && mfc.IsConnected)
                mfc.Close();
            if (tagId != string.Empty)
                Console.WriteLine($"Mifare Classic tag found: {sUid} {tagId}");
        }
    }
    if (tagId != string.Empty)
        Do stuff
}

当 phone 不支持 Mifare Classic 支持并且它不会修复所有设备时,这看起来是一种尝试启用 Mifare Classic 支持的超级 hacky 方式。

但是你的问题是黑客依赖于修改你的应用程序在你使用 old API enableForegrounDispatch

时从 Intent 创建的标签对象

这个旧的 API 还有一些其他问题,因为您使用的是更新更好的 enableReaderMode API(因为您使用的是 OnTagDiscovered 方法)直接获取Tag对象。

我还没有通过 Android 代码来解决所有这些问题,但我猜测它是某种形式的共享内存对象,尽管它被定义为“单向”回调。

因此,由于您的 App 进程不是 Tag 对象的创建者,因此您无法修改它。

因此,只有当您使用旧的 enableForegrounDispatch 时,您才可以使用此 hack,您可以在自己的应用程序 (Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);) 中 de-Parcel Parceled 对象并具有对 Tag 的完全访问权限对象,因为您的进程创建了它。

您的代码在使用 Mifare Classic Tool App 后工作的原因是,一旦标签硬件通过身份验证,它就会保持“已身份验证”状态,直到它从 RF 场中移除并失去电源。