安全与风控
在 Apple 平台用 CryptoTokenKit…
构建 macOS Token 驱动扩展、iOS/iPadOS NFC 智能卡会话、智能卡 APDU 通信、令牌钥匙串集成或证书式智能卡认证时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Apple 平台用 CryptoTo特定
解决思路
- 先判断使用模式:macOS 智能卡 token 扩展让硬件令牌的密码学项可供系统登录和钥匙串解锁;客户端查询令牌支持的钥匙串项;iOS/iPadOS 26+ 创建临时 NFC 智能卡插槽与接触式卡通信。
- 做边界路由:令牌/智能卡会话、令牌支持的钥匙串项、证书式智能卡认证归 CryptoTokenKit;passkeys/WebAuthn 和账号登录交给 authentication;Secure Enclave、CryptoKit 原语、钥匙串架构、证书固定和信任策略交给 swift-security。
- 按平台与版本选择 API:TKTokenDriver/TKToken/TKTokenSession 为 iOS 10+/macOS 10.12+;TKSmartCardTokenDriver 为 iOS 10+/macOS 10.12+,macOS 扩展流程;TKSmartCard/TKSmartCardSlotManager 为 iOS 9+/macOS 10.10+,default 可选;TKTokenWatcher 为 iOS 10+/macOS 10.12+;TKSmartCardSlotNFCSession 和 TKSmartCardTokenRegistrationManager 为 iOS/iPadOS 26+。
- 为 macOS 系统登录和钥匙串解锁创建 token driver app extension,宿主 app 只作为扩展的分发机制。
- 在扩展中实现三个核心类:TokenDriver 继承 TKSmartCardTokenDriver;Token 继承 TKSmartCardToken;TokenSession 继承 TKSmartCardTokenSession。
- 实现 TokenDriver 的 TKSmartCardTokenDriverDelegate 方法 tokenDriver(_:createTokenFor:aid:),根据 smartCard 和 aid 返回 Token,传入 instanceID(例如 com.example.token: 拼接 smartCard.slot.name)和 tokenDriver。
- 实现 Token 初始化:调用 super.init(smartCard:aid:instanceID:tokenDriver:),设置 delegate 为 self;从 smartCard 读取证书数据;用 SecCertificateCreateWithData 创建 SecCertificate,失败抛 TKError(.corruptedData)。
- 用 TKTokenKeychainCertificate(certificate:objectID:) 和 TKTokenKeychainKey(certificate:objectID:) 创建钥匙串内容项;设置 keyItem.canSign、canDecrypt、isSuitableForLogin;用 keychainContents?.fill(with:) 填充。
- 在 Token 中实现 createSession(_:) 返回 TokenSession(token:)。
- 配置扩展 Info.plist:NSExtension.NSExtensionAttributes 下 com.apple.ctk.driver-class = $(PRODUCT_MODULE_NAME).TokenDriver;NSExtensionPointIdentifier = com.apple.ctk-tokens。
- 安装时用宿主 app 以 _securityagent 启动一次来注册扩展:sudo -u _securityagent /Applications/TokenHost.app/Contents/MacOS/TokenHost。
- TokenSession 实现 TKTokenSessionDelegate:在 tokenSession(_:supports:keyObjectID:algorithm:) 中按操作和算法精确返回支持情况;例如 signData 支持 rsaSignatureDigestPKCS1v15SHA256 或 ecdsaSignatureDigestX962SHA256;decryptData 支持 rsaEncryptionOAEPSHA256;performKeyExchange 支持 ecdhKeyExchangeStandard;其他返回 false。
- 实现签名委托 tokenSession(_:sign:keyObjectID:algorithm:):获取 smartCard,用 smartCard.withSession 包裹,调用卡上签名例程 performCardSign。
- 实现解密委托 tokenSession(_:decrypt:keyObjectID:algorithm:):获取 smartCard,用 smartCard.withSession 包裹,调用卡上解密例程 performCardDecrypt。
- 需要 PIN 时,在 tokenSession(_:beginAuthFor:constraint:) 返回 TKTokenSmartCardPINAuthOperation;设置 pinFormat.charset = .numeric;设置 minPINLength 和 maxPINLength;设置 smartCard;设置 apduTemplate 为 Verify APDU;设置 pinByteOffset,例如 5。
- 用 TKSmartCard 做低层 APDU 通信;TKSmartCardSlotManager.default 可能为 nil,nil 表示硬件不可用、缺少 entitlement/access 或运行时能力不支持。
- 发现读卡器:guard let slotManager = TKSmartCardSlotManager.default;遍历 slotManager.slotNames;调用 getSlot(withName:);槽状态为 .validCard 时调用 slot.makeSmartCard();然后通信。
- 发送结构化 APDU:用 card.send(ins:p1:p2:data:le:);始终用 withSession 包裹;例如选择应用 ins:0xA4、p1:0x04、p2:0x00、data:aid、le:nil;检查 sw == 0x9000,否则抛 TKError(.communicationError)。
- 对原始 APDU 字节或非标准格式,用 transmit(_:reply:) 并手动管理 beginSession/endSession 生命周期。
- iOS/iPadOS 26+ NFC:先 guard TKSmartCardSlotManager.default 且 slotManager.isNFCSupported();调用 slotManager.createNFCSlot(message:completion:);在 completion 中处理 session/error,defer session.end();通过 session.slotName 和 slotManager.slotNamed(slotName) 获取 slot,再 slot.makeSmartCard(),然后用 card.send 通信。
- 用 TKSmartCardTokenRegistrationManager 在 iOS/iPadOS 26+ 注册 NFC 智能卡,以供后续钥匙串使用。
- 钥匙串集成:令牌存在时其项作为标准钥匙串条目暴露;用 kSecClass = kSecClassKey、kSecAttrTokenID = tokenID、kSecReturnRef = true 查询 SecItemCopyMatching 获取 SecKey;失败抛 TKError(.objectNotFound)。
- 跨启动持久引用用 kSecReturnPersistentRef 替代 kSecReturnRef;令牌移除后引用失效,遇 errSecItemNotFound 提示用户重新插入令牌。
- 查询证书同样用 kSecClass = kSecClassCertificate 和 kSecAttrTokenID。
- 证书认证要求:用户登录的令牌至少含一个能签名的密钥,支持 EC signature digest X962、RSA signature digest PSS 或 RSA signature digest PKCS1v15。
- 钥匙串解锁要求:256 位 EC 密钥 kSecAttrKeyTypeECSECPrimeRandom 支持 ecdhKeyExchangeStandard,或 2048/3072/4096 位 RSA 密钥 kSecAttrKeyTypeRSA 支持 rsaEncryptionOAEPSHA256 解密。
- macOS 在 com.apple.security.smartcard 域配置智能卡认证偏好:allowSmartCard 默认 true 启用;checkCertificateTrust 默认 0 信任级别 0-3;oneCardPerUser 默认 false 将单卡绑定账户;enforceSmartCard 默认 false 要求登录智能卡。
- 信任级别:0 信任所有;1 有效性加颁发者;2 加软吊销;3 加硬吊销。
- 用 TKTokenWatcher 监控令牌插入/移除;枚举 tokenIDs,安装插入处理器,并为每个观察到的令牌添加移除处理器;保持 watcher 存活。槽级读卡器状态用 Smart Card Slot Monitoring。
- 错误处理:CryptoTokenKit 抛 TKError;处理 .notImplemented、.communicationError、.corruptedData、.canceledByUser、.authenticationFailed、.objectNotFound、.tokenNotFound、.authenticationNeeded。
- 不要在没有检查令牌存在时查询令牌钥匙串项;用 TKTokenWatcher 的 tokenIDs 包含 savedTokenID 后再 findTokenKey,否则提示用户插入令牌。
- 不要把 API 可用性当作访问保证;不要强制解包 TKSmartCardSlotManager.default;先 guard 可用或可访问。
- 不要跳过智能卡通信的会话管理;不要直接 card.transmit 而不开 session;用 withSession 或 beginSession/endSession。
- 不要忽略 APDU 响应状态字;每次 card.send 后检查 sw == 0x9000,否则抛错误,例如 SmartCardError.commandFailed(statusWord: sw)。
- 不要硬编码一刀切算法支持;supports 委托必须反映硬件实际实现,不能无条件返回 true,否则系统尝试不支持操作会运行时失败。
- 验证精确能力的平台可用性:TKTokenWatcher iOS 10+,NFC 智能卡会话 iOS/iPadOS 26+。
- guard TKSmartCardSlotManager.default 缺失 entitlement、硬件或运行时支持。
- macOS token 扩展 target 必须使用 NSExtensionPointIdentifier = com.apple.ctk-tokens。
- Info.plist 中 com.apple.ctk.driver-class 必须设为正确 driver class。
- 安装时通过 _securityagent 启动注册扩展。
- TKTokenSessionDelegate 必须检查具体算法,不是 blanket true。
- 智能卡会话必须打开和关闭,使用 withSession 或 beginSession/endSession。
- 每次 send 调用后必须检查 APDU 状态字。
- 钥匙串查询前必须用 TKTokenWatcher 验证令牌存在。
- 必须处理 TKError 用例并给出适当用户反馈。
- keychain contents 必须用正确的 objectID 填充。
- TKTokenKeychainKey 能力 canSign、canDecrypt 必须匹配硬件。
- 按部署环境配置证书信任级别。
- 令牌移除时对持久引用处理 errSecItemNotFound。
- iOS 26+ NFC 会话必须用 TKSmartCardSlotNFCSession.end() 结束。
适用边界 · 注意事项
- 非 Apple 平台,或没有智能卡/NFC 硬件、相应 entitlement、macOS 扩展宿主和注册流程时,不适用。
- 需要 passkeys/WebAuthn、账号登录、Secure Enclave、CryptoKit 原语、钥匙串架构、证书固定或信任策略时,应使用对应框架而非 CryptoTokenKit。
- 仅做普通钥匙串存储或加密、不涉及硬件令牌/智能卡时,不适用。