TapLinx 调用 DESFire EV3 changeKey() 报 Integrity Error SW2=30
结论
代码创建的是 KeyType.TWO_KEY_THREEDES 应用,与描述中的 AES 应用不一致。创建应用、认证和更改密钥时,使用的密钥类型需要一致。
更改应用主密钥 key 0,应按以下顺序操作:
选择目标应用
使用该应用当前的 key 0 完成认证
调用 changeKey() 更改 key 0
使用新的 key 0 重新认证
PICC 主密钥认证成功,不能代替目标应用的 key 0 认证。题目中的执行记录没有明确显示“选择应用后,使用旧应用主密钥认证成功”这一步,需要先单独验证。
仅凭这段代码和异常栈,还无法确定 Integrity Error 的唯一原因。实际认证方式、密钥数据和调用 changeKey() 时的会话状态都需要检查。
SW2=30 表示什么
先确认日志使用的数值进制。如果 TapLinx 按十进制输出,30 就是十六进制的 0x1E,对应 DESFire 的 INTEGRITY_ERROR,不能把它直接当作 0x30。采用 ISO 包装的原始响应通常为:
91 1E
完整性错误表示命令处理中的完整性校验失败。ChangeKey 的处理涉及认证建立的会话、加密数据和校验内容,需要检查:
- 是否已在当前应用中正确完成认证。
- 认证算法、认证模式与应用密钥类型是否匹配。
- 认证后是否重新选择应用或重新连接卡片,导致会话失效。
- 传入的密钥数据格式是否符合当前 TapLinx 接口要求。
缺少认证、密钥错误或权限不足,也可能在不同阶段返回其他错误码。仅凭 Integrity Error,不能断定是“旧密钥错误”或“少了一次认证”。
应用主密钥与 PICC 主密钥需要分别认证
PICC 层与应用层各有自己的主密钥。即使编号都是 0,它们也属于不同的上下文。
例如:
选择 PICC
使用 PICC key 0 认证
创建应用
选择目标应用
使用应用 key 0 认证
更改应用 key 0
selectApplication() 成功只表示选中了应用,不会自动完成应用密钥认证,也不能沿用此前 PICC 层的认证权限。
创建参数:
.setAppMasterKeyChangeable(true)
允许更改应用主密钥,但更改前仍需认证。文件管理和目录访问的认证设置,也不能代替更改主密钥所需的认证。
按照 2K3DES 配置更改 key 0
如果保留当前创建参数,就应始终按 TWO_KEY_THREEDES 处理该应用。
对于采用标准初始密钥、尚未更改密钥的新应用,2K3DES 初始密钥通常为 16 字节全零。如果卡片经过个性化,或默认应用密钥配置已被修改,则应使用实际初始密钥,不能假定为全零。
示例中的 desfire 和 AID 沿用现有代码。authenticateKey0 是调用方提供的认证函数,不是 TapLinx API;它必须对当前应用的 key 0 执行 2K3DES 认证,并在失败时抛出异常。
fun change2K3DesApplicationMasterKey(
oldKey: ByteArray,
newKey: ByteArray,
authenticateKey0: (ByteArray) -> Unit
) {
// 此处按 16 字节的 2K3DES 原始密钥表示。
require(oldKey.size == 16)
require(newKey.size == 16)
desfire.selectApplication(AID)
// 使用当前应用的旧 key 0 建立认证会话。
authenticateKey0(oldKey.copyOf())
desfire.changeKey(
0,
KeyType.TWO_KEY_THREEDES,
oldKey.copyOf(),
newKey.copyOf(),
0x00
)
// 更改当前认证密钥后,重新建立认证会话。
desfire.selectApplication(AID)
authenticateKey0(newKey.copyOf())
}
认证回调应使用当前 TapLinx 版本支持的 authenticate() 重载、认证模式和密钥对象格式。题目未提供 SDK 版本和认证代码,因此无法给出保证能编译的认证调用。
接入时需要确认:
- 认证的密钥编号是应用 key
0。 - 密钥对象按 2K3DES 构造。
- 认证模式与所用 DESFire 命令流程匹配。
- 认证失败后立即停止,不再调用
changeKey()。 - 从认证成功到调用
changeKey()之间,不重新选择应用或重建连接。
如果 SDK 的密钥容器要求特定的 3DES 展开格式,应按该版本文档构造容器。不要把底层加密库的格式要求直接套到 changeKey() 的字节数组参数上。
changeKey() 参数应如何理解
题目中的调用形式为:
desfire.changeKey(
keyNo,
KeyType.TWO_KEY_THREEDES,
oldKey,
newKey,
0x00
)
各参数在本场景中的要求如下:
| 参数 | 本场景中的要求 |
|---|---|
keyNo | 更改应用主密钥时为 0 |
| 密钥类型 | 与当前应用及目标密钥类型一致 |
oldKey | 当前应用 key 0 的实际旧密钥 |
newKey | 要写入的新密钥,长度和编码符合接口要求 |
0x00 | 按该重载定义传入的密钥版本值,不是认证模式或密钥编号 |
更改当前认证密钥和更改其他密钥时,底层 ChangeKey 报文的构造有所不同。使用 TapLinx 高层接口时,这些差异应由 SDK 处理。不要自行对 oldKey、newKey 做异或,也不要提前拼接 CRC、填充或加密报文。
更改当前用于认证的 key 0 后,后续操作需要使用新密钥重新认证,不能继续依赖旧认证会话。
AES 与 TWO_KEY_THREEDES 的差异会导致失败吗
如果实际代码混用了这两种算法,就会导致失败。这行配置创建的是 2K3DES 应用:
.setKeyTypeOfApplicationKeys(KeyType.TWO_KEY_THREEDES)
随后若按 AES 方式认证,即使双方都使用 16 字节全零密钥,也无法匹配。AES-128 与 2K3DES 的原始密钥长度都可以是 16 字节,但长度相同不代表算法相同。
如果目标是 AES 应用,以下配置需要同时统一:
- 创建应用时的 AES 密钥类型配置。
- AES 密钥对象与对应认证方式。
changeKey()的密钥类型及密钥数据。
AES 枚举的具体名称以当前 TapLinx 版本为准。仅修改日志中的 KEY_TYPE 变量,或只修改 changeKey() 的第二个参数,都不会自动将已有的 2K3DES 应用转换为 AES 应用。
仍然失败时,如何缩小范围
先单独验证“选择应用后,使用旧 key 0 认证”。认证成功后,再执行一次 changeKey(),并记录实际 AID、密钥编号、密钥类型、认证模式和原始响应状态字。认证未成功时,不执行密钥更改;日志中也不要输出密钥本身。
如果仍报完整性错误,优先检查认证后是否重新选卡、重新选择应用,或发生了其他会话变化,再核对 TapLinx 的具体版本及对应接口文档。
异常栈中的 DESFireEV2.changeKey() 只能说明调用经过了该实现路径,不能据此认定 IDESFireEV1 不支持当前操作,也不能直接认定存在 EV3 兼容性缺陷。
如果更改命令期间断连或响应丢失,卡片上的密钥状态可能已经改变。重新连接后,应分别验证已知的旧密钥和新密钥,确认实际状态,再决定下一步操作。formatPICC() 会清除卡片应用和数据,不应作为密钥更改失败后的常规重试步骤。
备注:内容仅供参考。
