Node.js+ACR122U开发NTAG213密码保护时出现异常半锁定问题求助
Node.js+ACR122U开发NTAG213密码保护时出现异常半锁定问题求助
看起来你遇到的核心问题有两个:一是设置密码后卡片直接进入不可写且无法正常解锁的异常状态,二是移除密码时的认证命令格式错误导致无法通过认证修改配置。我结合NTAG213的 datasheet 和ACR122U的通信规范,帮你梳理问题并给出修复方案:
问题根源拆解
1. 密码认证命令的APDU格式完全错误
你构造的认证APDU存在长度不匹配的问题,这会导致卡片直接拒绝认证请求,自然无法在认证后修改PWD、AUTH0等受保护页面:
- 你把
Lc字段设为0x05,但后续的Data实际长度远大于5字节,APDU格式不合法,卡片不会响应这个无效命令。 - 另外,ACR122U的Escape命令(
D4 42)搭配Transceive子命令(0x01)时,需要正确包裹卡片的PWD_AUTH指令,不能省略或乱填长度。
2. 密码相关页面的写入顺序逻辑错误
你先设置了AUTH0(触发页面保护),再写入PWD和PACK,但AUTH0=0x04会让Page4及之后的页面(包括PWD所在的Page43)变成受保护状态,此时未认证的写操作会被卡片拒绝,相当于刚设置完保护就把密码配置页面锁死了。
修复后的完整代码
1. 修正/nfc/setpassword接口(调整写入顺序+合理验证)
app.post('/nfc/setpassword', async (req, res) => { const { password } = req.body; if (typeof password !== 'string' || password.length !== 4) { return res.status(400).json({ error: 'Password must be a 4-character string' }); } if (!currentReader || !currentCard) { return res.status(400).json({ error: 'No card present' }); } try { const pwd = Buffer.from(password, 'utf8'); // "1234" → 31 32 33 34 const pack = Buffer.from([0x84, 0x00, 0x00, 0x00]); // PACK(仅前2字节有效,后2字节为保留位) const access = Buffer.from([0x00, 0x00, 0x00, 0x00]); // PROT=0:未认证只读,认证后可读写 const auth0 = Buffer.from([0x04, 0x00, 0x00, 0x00]); // 从Page4开始保护所有后续页面 // 核心:先写PWD/PACK(此时AUTH0未设置,页面无保护),再触发权限保护 await currentReader.write(0x2B, pwd); // PWD (Page43) await currentReader.write(0x2C, pack); // PACK (Page44) // 验证PWD写入(此时还未触发保护,可直接读取) const verifyPwd = await currentReader.read(0x2B, 4); if (!verifyPwd.equals(pwd)) { return res.status(500).json({ error: 'Password write verification failed' }); } // 最后写入权限配置,正式开启保护 await currentReader.write(0x2A, access); // ACCESS (Page42) await currentReader.write(0x29, auth0); // AUTH0 (Page41) // 可选:用刚设置的密码做一次认证测试,确认配置生效 const testAuthCmd = Buffer.concat([ Buffer.from([0xFF, 0x00, 0x00, 0x00, 0x08]), Buffer.from([0xD4, 0x42, 0x01, 0x1B]), pwd, Buffer.from([0x02]) ]); const testResp = await currentReader.transmit(testAuthCmd, 10); if (testResp.length <5 || testResp[0]!==0xD5 || testResp[1]!==0x43 || testResp[2]!==0x01) { return res.status(500).json({ error: 'Authentication test failed, configuration may be invalid' }); } return res.status(200).json({ success: true, message: 'Password set successfully' }); } catch (err) { return res.status(500).json({ error: 'Failed to set password', detail: err.message }); } });
2. 修正/nfc/removepassword接口(合法APDU+正确响应解析)
app.post('/nfc/removepassword', async (req, res) => { const { password } = req.body; if (typeof password !== 'string' || password.length !== 4) { return res.status(400).json({ error: 'Password must be a 4-character string' }); } if (!currentReader || !currentCard) { return res.status(400).json({ error: 'No card present' }); } try { const pwdBuf = Buffer.from(password, 'utf8'); // 构造合法的PWD_AUTH认证APDU const authCmd = Buffer.concat([ Buffer.from([0xFF, 0x00, 0x00, 0x00, 0x08]), // CLA+INS+P1+P2+Lc Buffer.from([0xD4, 0x42, 0x01, 0x1B]), // Escape命令+Transceive子命令+卡片PWD_AUTH指令 pwdBuf, Buffer.from([0x02]) // 期望接收2字节PACK响应 ]); const response = await currentReader.transmit(authCmd, 10); // 解析ACR122U的响应:格式为D5 43 01 [PACK1] [PACK2] 90 00 if (response.length < 5 || response[0] !== 0xD5 || response[1] !== 0x43 || response[2] !== 0x01) { return res.status(401).json({ error: 'Authentication failed: invalid card response' }); } const receivedPack = response.slice(3, 5); const expectedPack = Buffer.from([0x84, 0x00]); // 对应你设置的PACK值 if (!receivedPack.equals(expectedPack)) { return res.status(401).json({ error: 'Invalid password (PACK mismatch)' }); } // 认证成功后,清空所有密码相关配置 await currentReader.write(0x29, Buffer.from([0xFF, 0x00, 0x00, 0x00])); // AUTH0设为0xFF(取消所有保护) await currentReader.write(0x2A, Buffer.from([0x00, 0x00, 0x00, 0x00])); // 重置ACCESS配置 await currentReader.write(0x2B, Buffer.from([0x00, 0x00, 0x00, 0x00])); // 清空密码 await currentReader.write(0x2C, Buffer.from([0x00, 0x00, 0x00, 0x00])); // 清空PACK return res.status(200).json({ success: true, message: 'Password removed successfully' }); } catch (err) { return res.status(500).json({ error: 'Failed to remove password', detail: err.message }); } });
额外注意事项
- NTAG213的权限逻辑:
- 你设置的
PROT=0完全符合需求:未认证时受保护页面(Page4及之后)只读,认证后可读写; AUTH0=0x04会保护从Page4到最后一页的所有内容,包括用户数据区和密码配置区,这和NFC Tools的行为一致。
- 你设置的
- 异常恢复:如果卡片已经被锁死,只能通过NFC Tools的“硬重置”功能(擦除整个卡片)来恢复,这是NTAG芯片的硬件保护机制,无法通过软件绕过。
内容来源于stack exchange




