NFC MIFARE Classic 1K无法读写。

3
我有一台NFC读卡器和MIFARE Classic 1K卡片。我有一个Visual C# winforms项目。现在,我能够连接到读卡器并检测到卡片并获取其UUID。我遇到的问题是在写入和读取数据时。我在互联网上搜索了很多,并找到了一些解决方案,甚至测试了SDK提供的演示代码...但都没有起作用。
让我描述一下我用于写入、认证块、发送APDU和读取块的工作流程和代码。
以下是写入数据到块5的代码。
String tmpStr = Text;
            int indx;
            if (authenticateBlock(Block))
            {
                ClearBuffers();
                SendBuff[0] = 0xFF;                             // CLA
                SendBuff[1] = 0xD6;                             // INS
                SendBuff[2] = 0x00;                             // P1
                SendBuff[3] = (byte)int.Parse(Block);           // P2 : Starting Block No.
                SendBuff[4] = (byte)int.Parse("16");            // P3 : Data length
                SendBuff[5] = 0xFF;
                SendBuff[6] = 0xFF;
                SendBuff[7] = 0xFF;
                SendBuff[8] = 0xFF;
                SendBuff[9] = 0xFF;
                SendBuff[10] = 0xFF;

for (indx = 0; indx <= (tmpStr).Length - 1; indx++)
                    {
                        SendBuff[indx + 5] = (byte)tmpStr[indx];
                    }
                    SendLen = SendBuff[4] + 5;
                    RecvLen = 0x02;

                    retCode = SendAPDUandDisplay(2);

                    if (retCode != Card.SCARD_S_SUCCESS)
                    {
                        MessageBox.Show("fail write");

                    }
                    else
                    {
                        MessageBox.Show("write success");
                    }
                }
                else
                {
                    MessageBox.Show("FailAuthentication");
                }

                CloseCardConnection();

函数SendAPDUandDisplay的代码如下:
private int SendAPDUandDisplay(int reqType)
        {
            int indx;
            string tmpStr = "";

            pioSendRequest.dwProtocol = Aprotocol;
            pioSendRequest.cbPciLength = 8;

            //Display Apdu In
            for (indx = 0; indx <= SendLen - 1; indx++)
            {
                tmpStr = tmpStr + " " + string.Format("{0:X2}", SendBuff[indx]);
            }

            retCode = Card.SCardTransmit(hCard, ref pioSendRequest, ref SendBuff[0],
                                 SendLen, ref pioSendRequest, ref RecvBuff[0], ref RecvLen);

            if (retCode != Card.SCARD_S_SUCCESS)
            {
                return retCode;
            }

            else
            {
                try
                {
                    tmpStr = "";
                    switch (reqType)
                    {
                        case 0:
                            for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            if ((tmpStr).Trim() != "90 00")
                            {
                                //MessageBox.Show("Return bytes are not acceptable.");
                                return -202;
                            }

                            break;

                        case 1:

                            for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
                            {
                                tmpStr = tmpStr + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            if (tmpStr.Trim() != "90 00")
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            else
                            {
                                tmpStr = "ATR : ";
                                for (indx = 0; indx <= (RecvLen - 3); indx++)
                                {
                                    tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                                }
                            }

                            break;

                        case 2:

                            for (indx = 0; indx <= (RecvLen - 1); indx++)
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            break;
                    }
                }
                catch (IndexOutOfRangeException)
                {
                    return -200;
                }
            }
            return retCode;
        }

函数authenticateBlock的代码如下所示。
private bool authenticateBlock(String block)
        {
            ClearBuffers();
            /*SendBuff[0] = 0xFF;                         // CLA
            SendBuff[2] = 0x00;                         // P1: same for all source types 
            SendBuff[1] = 0x82;                         // INS: for stored key input
            SendBuff[3] = 0x00;                         // P2 : Memory location;  P2: for stored key input
            SendBuff[4] = 0x05;                         // P3: for stored key input
            SendBuff[5] = 0x01;                         // Byte 1: version number
            SendBuff[6] = 0x00;                         // Byte 2
            SendBuff[7] = (byte)int.Parse(block);       // Byte 3: sectore no. for stored key input
            SendBuff[8] = 0x60;                         // Byte 4 : Key A for stored key input
            SendBuff[9] = (byte)int.Parse("1");         // Byte 5 : Session key for non-volatile memory
            */

            SendBuff[0] = 0xD4;
            SendBuff[1] = 0x4A;
            SendBuff[2] = 0x01;
            SendBuff[3] = 0x00;
            SendBuff[4] = (byte) int.Parse(block);
            SendBuff[5] = 0xFF;
            SendBuff[6] = 0xFF;
            SendBuff[7] = 0xFF;
            SendBuff[8] = 0xFF;
            SendBuff[9] = 0xFF;
            SendBuff[10] = 0xFF;


            /*SendLen = 0x0A;
            RecvLen = 0x02;*/

            SendLen = 4;
            RecvLen = 255;


            retCode = SendAPDUandDisplay(2);

            if (retCode != Card.SCARD_S_SUCCESS)
            {
                //MessageBox.Show("FAIL Authentication!");
                return false;
            }

            return true;
        }

One strange thing to notice here is that no matter what values are set in sendBuff, this function always returns a true value and the "write data" code block (the very first code block) returns a message of "write success". However, after executing the "write data" code, when attempting to read block 5 (in my case), there is no data present. The "read block" code block returns an empty string. To double-check whether the data was written but my code was unable to read it, I used external software to verify the added value. However, even the external software did not show the data that was supposedly successfully written.
The following is the code used to read block 5:
public string readBlock(String Block)
        {
            string tmpStr = "";
            int indx;

            if (authenticateBlock(Block))
            {
                ClearBuffers();
                /*
                SendBuff[0] = 0xFF; // CLA 
                SendBuff[1] = 0xB0;// INS
                SendBuff[2] = 0x00;// P1
                SendBuff[3] = (byte)int.Parse(Block);// P2 : Block No.
                SendBuff[4] = (byte)int.Parse("16");// Le
                */

                SendBuff[0] = 0xD4;
                SendBuff[1] = 0x40;
                SendBuff[2] = 0x01;
                SendBuff[3] = 0x30;
                SendBuff[4] = byte.Parse(Block.ToString(), System.Globalization.NumberStyles.HexNumber);
                SendBuff[5] = 0xFF;
                SendBuff[6] = 0xFF;
                SendBuff[7] = 0xFF;
                SendBuff[8] = 0xFF;
                SendBuff[9] = 0xFF;
                SendBuff[10] = 0xFF;

                //SendLen = 5;
                //RecvLen = SendBuff[4] + 2;

                SendLen = 5;
                RecvLen = 255;

                retCode = SendAPDUandDisplay(2);

                if (retCode == -200)
                {
                    return "outofrangeexception";
                }

                if (retCode == -202)
                {
                    return "BytesNotAcceptable";
                }

                if (retCode != Card.SCARD_S_SUCCESS)
                {
                    return "FailRead";
                }

                // Display data in text format
                for (indx = 0; indx <= RecvLen - 1; indx++)
                {
                    tmpStr = tmpStr + Convert.ToChar(RecvBuff[indx]);
                }

                return (tmpStr);
            }
            else
            {
                return "FailAuthentication";
            }
        }

请注意,read block方法在检查是否连接读卡器后调用,如果是,则调用readblock方法并返回一个空字符串。
我尝试了几个值,如您在注释中看到的,但没有任何帮助,已经困扰了我三天。
请问有人能帮我找出问题所在,并指导我应该发送什么值以验证块?
如果有人了解我的代码问题或想更正我在sendBuff[]中设置的值,请以C#代码形式引用它们,以便我可以完全实现您要求的解决方案。
非常感谢任何真诚的帮助。

你确定你正在使用正确的SDK吗?看起来你正在使用读取智能卡而不是NFC令牌的东西。你正在使用哪个SDK? - S.Spieker
@S.Spieker 我收到了ACR122U SDK,它的驱动程序和示例,我使用了Visual C#其中之一。您可以在供应商网站上查看该产品,链接为https://www.nxp.com/products/rfid-nfc/mifare-hf/mifare-classic:MC_41863。 - Ibrahim Iqbal
似乎没有公开可用的东西,所以即使是猜测也很困难。 - S.Spieker
@S.Spieker你之前有无NFC智能卡的非接触式读写经验? - Ibrahim Iqbal
请查看Feig的OBIDISC4NET-SDK。 - Felix Arnold
2个回答

0

我只是用Arduino实验过Mifare 1K。

在这个例子中,在检测到卡片并获取UUID后,需要在读写之前选择卡片。你在执行这个选择卡片的步骤吗?


是的,我获取了读卡器的UUID,然后检测卡片。问题在于,在进行块认证、读取和写入时发送APDU字节时出现了问题...所有这些操作都需要发送一些APDU字节。我想我只需要知道在认证块时发送的那16个字节作为APDU即可。 - Ibrahim Iqbal

0
由于它是S50 1K经典卡,因此“字节映射”不同,虽然过程周期表面上相同,但您需要通过获取ATR / ATS并解析它以检索开关设置来检查它是否为S50。对于接触式卡片,其为ATR,对于非接触式卡片,其为ATS,但技术上来说是相同的。在PCSC下,这是在询问读卡器是否插入卡片之前询问读卡器状态的请求,之后才发送APDU。您也可以同时获取其他设置。 对于MFS50,您需要执行'S'选择,然后使用扇区密钥进行'L'登录,然后读取该扇区的前4个块中的前三个块,但忽略第一个块的大部分内容 - 密钥位于第四个块中,并带有其他控制字节。在'S'选择时返回'UID',在'L'登录时返回成功或失败,在读取扇区块中的数据或'E'错误时返回数据。对于14443扇区0,第一个块被“制造”数据所占用,其取决于构造方式,可以具有4、7或12字节作为UID,并且从数据视角嵌入CRC和校验字节,因此不能用作UID - 它是数据映射。 Lights、C、Ultralights、EV1具有不同的“字节映射”,可能在卡或扇区具有'L'登录或没有。这些是14443类型,还有15693类型,其扇区仅包含数据。一些中国的14443卡片具有可写入制造块,但通常是静态、设置和OTP混合(一次位设置不能取消设置,用于身份验证和NFC大小验证!)。
Classic 1K: ident UID: 66908648, requires sector key log in (A read, B 
read/write)    
Sector 0:
6690864838880400468f76594d100612
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff078069ffffffffffff
...
Sector 15:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff0780bcffffffffffff

Mifare ultralight: UID 0489d802a44080, might require sector login but key held elsewhere.
Sector 0:
0489D8DD
02A44080
66480900
00010001
.
Sector 1:
00000000
00000000
00000000
000000EE

15693: UID D89A1F24500104E0
Sector 0:
50555955
Sector 1:
48485353
Sector 2:
59435300
Sector 3:
00000000
...
Sector 15:
00000000

因此,获取ATR / ATS并确定您拥有的卡片类型,然后相应地处理。哦,还要使用腰带、背带和一根绳子的方法 - 在写入卡片后再次读取以将所写内容与预期内容进行比较。15693需要完整扇区写入,否则该扇区中不会写入任何内容。这是否为Type 2 NFC / NDEF - 还有其他标准。已将Zebra打印机改装为可在运行时对NTAG201进行编码和打印。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接