You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NTAG21X(含NTAG215)密码写保护设置异常求助

NTAG215密码写保护失效问题排查

我编写了一段为NTAG设置密码写保护的代码,通过NFC Tools的内存读取功能已确认密码保护状态生效且写保护开启,但使用自研代码及NFC Tools仍可无需密码写入记录。目标是实现无密码无法写入任何记录的效果,当前测试对象为NTAG215,求排查问题根源。

相关代码

static Future setPasswordWithTag({required NfcTag tag, required String password}) async {
  bool statusOfTag = false;

  final pass = password.codeUnits;
  if (pass.length != 4) {
    log('Password length should be 4 characters');
    return statusOfTag;
  }

  MiFare? c = MiFare.from(tag);
  NfcA? nfcA = NfcA.from(tag);
  var ndef = Ndef.from(tag);

  final tagSize = ndef!.maxSize;
  Uint8List? writePasswordApu, writePackApu, setAuth0Apu;

  if (tagSize <= 144) {
    // NTAG213
    print("NTAG213");
    print(pass);
    final writePassword = [
      0xA2,
      0x2B,
      int.parse('0x${pass[0]}'),
      int.parse('0x${pass[1]}'),
      int.parse('0x${pass[2]}'),
      int.parse('0x${pass[3]}')
    ];
    final writePack = [0xA2, 0x2C, int.parse('0x${pass[0]}'), int.parse('0x${pass[1]}'), 0xFF, 0xFF];
    final setAuth0 = [
      0xA2, 0x29,
      0x00, // PROT = 0 (仅写入需密码)
      0x00,
      0x00,
      0x2B  // AUTH0 = 0x2B (从此页开始保护)
    ];

    writePasswordApu = Uint8List.fromList(writePassword);
    writePackApu = Uint8List.fromList(writePack);
    setAuth0Apu = Uint8List.fromList(setAuth0);
  } else if (tagSize > 144 && tagSize <= 520) {
    // NTAG215
    print("NTAG215");
    final writePassword = [
      0xA2,
      0x85, // Password page for NTAG215
      int.parse('0x${pass[0]}'),
      int.parse('0x${pass[1]}'),
      int.parse('0x${pass[2]}'),
      int.parse('0x${pass[3]}')
    ];

    print(writePassword);

    final writePack = [0xA2, 0x86, 0x12, 0x34, 0xFF, 0xFF]; // PACK

    final setAuth0 = [
      0xA2, 0x83, // ACCESS + AUTH0 config
      0x00, // PROT = 0 (仅写保护)
      0x00,
      0x00,
      0x85  // AUTH0 = 0x85 (从密码页开始保护)
    ];

    writePasswordApu = Uint8List.fromList(writePassword);
    writePackApu = Uint8List.fromList(writePack);
    setAuth0Apu = Uint8List.fromList(setAuth0);
  } else if (tagSize > 520) {
    // NTAG216
    print("NTAG216");
    final writePassword = [
      0xA2,
      0xE5,
      int.parse('0x${pass[0]}'),
      int.parse('0x${pass[1]}'),
      int.parse('0x${pass[2]}'),
      int.parse('0x${pass[3]}')
    ];
    final writePack = [0xA2, 0xE6, 0x5D, 0x6E, 0xFF, 0xFF];
    final setAuth0 = [
      0xA2, 0xE3,
      0x00, // PROT = 0
      0x00,
      0x00,
      0xE5  // AUTH0 = 0xE5
    ];

    writePasswordApu = Uint8List.fromList(writePassword);
    writePackApu = Uint8List.fromList(writePack);
    setAuth0Apu = Uint8List.fromList(setAuth0);
  }

  try {
    if (Platform.isIOS) {
      await c!.sendMiFareCommand(writePasswordApu!);
      await c.sendMiFareCommand(writePackApu!);
      await c.sendMiFareCommand(setAuth0Apu!);
    } else if (Platform.isAndroid) {
      var a= await nfcA!.transceive(data: writePasswordApu!);
      var b = await nfcA.transceive(data: writePackApu!);
      var c = await nfcA.transceive(data: setAuth0Apu!);

      print("PWD write: $a");
      print("PACK write: $b");
      print("ACCESS/AUTH0 write: $c");
    }

    await checkPasswordProtection(tag: tag);
    statusOfTag = true;
  } catch (e) {
    print(e.toString());
    statusOfTag = false;
  }

  return statusOfTag; 
}

问题根源及修复方案

1. AUTH0配置错误(核心问题)

NTAG215的用户数据页范围是0x04到0x84,你代码中设置AUTH0 = 0x85,仅会对密码页(0x85)及之后的配置页启用认证,所有用户数据页都不在保护范围内,因此无需密码即可写入。

修复:将NTAG215的AUTH0值改为0x00,从页0开始(包含所有用户数据页)强制认证写入:

final setAuth0 = [
  0xA2, 0x83,
  0x00, // PROT = 0 (仅写入需密码)
  0x00,
  0x00,
  0x00  // AUTH0 = 0x00 (保护所有页)
];

2. 密码写入逻辑错误

代码中int.parse('0x${pass[0]}')的转换逻辑错误:password.codeUnits返回的是字符的ASCII整数值(比如字符"1"的codeUnit是49),而int.parse('0x49')会将其解析为十六进制的73,导致实际设置的密码与输入不符。

修复:直接使用pass数组的原始整数值:

final writePassword = [
  0xA2,
  0x85,
  pass[0],
  pass[1],
  pass[2],
  pass[3]
];

3. PACK值建议与密码关联

当前代码中PACK值写死为0x1234,虽不影响认证功能,但建议将PACK设置为与密码相关的数值,增强安全性:

final writePack = [0xA2, 0x86, pass[0], pass[1], 0xFF, 0xFF];

4. 验证写入结果

Android平台下,NTAG写命令成功会返回Uint8List.fromList([0x0A]),可在代码中添加判断逻辑确认配置是否生效:

if (!listEquals(a, Uint8List.fromList([0x0A]))) {
  print("密码页写入失败");
  statusOfTag = false;
  return statusOfTag;
}

内容的提问来源于stack exchange,提问作者Csaba Gergely

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:24:56