CryptoTokenKit 用于访问安全令牌及其存储的加密资产。资料以 Swift 6.3 为目标,覆盖令牌驱动扩展、智能卡通信、令牌会话、钥匙串集成以及基于证书的身份验证。
一、平台可用性 CryptoTokenKit 主要是 macOS 框架。智能卡读卡器访问(TKSmartCard、TKSmartCardSlotManager)需要 macOS。令牌扩展 API(TKTokenDriver、TKToken、TKTokenSession)仅限 macOS。客户端令牌监视(TKTokenWatcher)与按 kSecAttrTokenID 过滤的钥匙串查询在 iOS 14+/macOS 11+ 可用。NFC 智能卡插槽会话在 iOS 16.4+ 可用。
二、架构概览 CryptoTokenKit 将硬件安全令牌(智能卡、USB 令牌)与 macOS 认证及钥匙串服务桥接,有两种主要使用模式:
- 令牌驱动扩展(仅 macOS):应用扩展,使硬件令牌的加密项对系统可用,驱动负责令牌生命周期、会话管理与加密操作。
- 客户端令牌访问(macOS + iOS):应用通过钥匙串查询由令牌支持的项;令牌存在时 CryptoTokenKit 会自动将令牌项暴露为标准钥匙串条目。
关键类型及其角色与平台:TKTokenDriver 为令牌驱动扩展基类(macOS);TKToken 表示硬件加密令牌(macOS);TKTokenSession 管理令牌认证状态(macOS);TKSmartCardTokenDriver 为智能卡扩展入口点(macOS);TKSmartCard 为底层智能卡通信(macOS);TKSmartCardSlotManager 发现并管理读卡器插槽(macOS);TKTokenWatcher 观察令牌插入与移除(macOS、iOS 14+);TKTokenKeychainKey 为令牌上存储的密钥(macOS);TKTokenKeychainCertificate 为令牌上存储的证书(macOS)。
三、令牌扩展 令牌驱动是一个 macOS 应用扩展,使硬件令牌的加密能力对系统可用;宿主应用仅作为扩展的投递机制。智能卡令牌扩展有三个核心类:TokenDriver(TKSmartCardTokenDriver 子类,入口点)、Token(TKSmartCardToken 子类,表示令牌)、TokenSession(TKSmartCardTokenSession 子类,处理操作)。 驱动类示例中,tokenDriver(:createTokenFor:aid:) 返回以 smartCard、aid、instanceID、tokenDriver 初始化的 Token。 Token 类从硬件读取证书与密钥并填充其钥匙串内容:先读取证书数据,用 SecCertificateCreateWithData 生成证书,再创建 TKTokenKeychainCertificate 与 TKTokenKeychainKey(设置 canSign、canDecrypt、isSuitableForLogin),并通过 keychainContents?.fill(with:) 填充;createSession(:) 返回 TokenSession。 Info.plist 与注册:扩展的 Info.plist 需指定驱动类,NSExtensionAttributes 下 com.apple.ctk.driver-class 设为 $(PRODUCT_MODULE_NAME).TokenDriver,NSExtensionPointIdentifier 为 com.apple.ctk-tokens。通过以 _securityagent 启动宿主应用完成一次性注册:sudo -u _securityagent /Applications/TokenHost.app/Contents/MacOS/TokenHost。
四、令牌会话 TKTokenSession 管理认证状态并经其 delegate 执行加密操作。示例中实现了 supports 方法按操作类型判断支持的具体算法(signData 支持 rsaSignatureDigestPKCS1v15SHA256、ecdsaSignatureDigestX962SHA256;decryptData 支持 rsaEncryptionOAEPSHA256;performKeyExchange 支持 ecdhKeyExchangeStandard,其余返回 false),并实现 sign 与 decrypt 方法,通过 getSmartCard() 获取卡片并在 withSession 中调用卡片操作。 PIN 认证:在 beginAuthFor 返回 TKTokenAuthOperation,示例使用 TKTokenSmartCardPINAuthOperation,设置 pinFormat 的 charset 为 numeric、minPINLength 为 4、maxPINLength 为 8,指定 smartCard、apduTemplate(buildVerifyAPDU())与 pinByteOffset 为 5。
五、智能卡通信 TKSmartCard 提供与读卡器所连智能卡的底层 APDU 通信(仅 macOS)。 发现读卡器:通过 TKSmartCardSlotManager.default 检查服务可用性,遍历 slotNames,用 getSlot(withName:) 获取插槽,当 slot.state == .validCard 时通过 slot.makeSmartCard() 取得卡片并通信。 发送 APDU 命令:使用 send(ins:p1:p2:data:le:) 进行结构化 APDU 通信,并始终用 withSession 包裹。示例 selectApplication 中发送 ins 0xA4、p1 0x04、p2 0x00,并检查状态字是否等于 0x9000,否则抛出 TKError(.communicationError)。对于原始 APDU 字节或非标准格式,可使用 transmit(_:reply:) 并手动管理 beginSession/endSession 生命周期。 NFC 智能卡会话(iOS 16.4+):在支持的 iOS 设备上,检查 slotManager.isNFCSupported(),调用 createNFCSlot(message:) 创建会话,结束会话时调用 session.end(),再通过 slotName 取得插槽与卡片并用 card.send(...) 通信。
六、钥匙串集成 令牌存在时,CryptoTokenKit 将其项暴露为标准钥匙串条目。可用 kSecAttrTokenID 属性查询,如示例中构造 kSecClass 为 kSecClassKey、kSecAttrTokenID 为 tokenID、kSecReturnRef 为 true 的查询,用 SecItemCopyMatching 获取 SecKey,不成功则抛出 TKError(.objectNotFound)。可使用 kSecReturnPersistentRef 代替 kSecReturnRef 以获得跨应用启动存活的持久引用;令牌移除后引用失效,需处理 errSecItemNotFound 并提示用户重新插入令牌。证书可用 kSecClass: kSecClassCertificate 同样方式查询。
七、证书身份验证 令牌密钥要求:用于用户登录时,令牌至少需包含一个能进行 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 域(MDM 或系统级)配置。键包括 allowSmartCard(默认 true,启用智能卡认证)、checkCertificateTrust(默认 0,证书信任级别 0-3)、oneCardPerUser(默认 false,将单张智能卡配对到账户)、enforceSmartCard(默认 false,要求登录使用智能卡)。信任级别:0 信任全部、1 有效性 + 颁发者、2 增加软吊销、3 增加硬吊销。
八、令牌监视 TKTokenWatcher 监视令牌插入与移除,macOS 与 iOS 14+ 均可用。示例 TokenMonitor 中遍历 watcher.tokenIDs 打印令牌,用 watcher.tokenInfo(forTokenID:) 获取 driverName 与 slotName,并通过 setInsertionHandler 处理插入、addRemovalHandler(_:forTokenID:) 处理移除。
九、错误处理 CryptoTokenKit 操作抛出 TKError。关键错误码:.notImplemented(此令牌不支持该操作)、.communicationError(与令牌通信失败)、.corruptedData(来自令牌的数据损坏)、.canceledByUser(用户取消操作)、.authenticationFailed(PIN 或密码错误)、.objectNotFound(未找到请求的密钥或证书)、.tokenNotFound(令牌不再存在)、.authenticationNeeded(操作前需要认证)。
十、常见错误 不要:不检查令牌存在就查询令牌钥匙串项(应先通过 TKTokenWatcher 确认令牌仍在,否则提示用户插入)。不要:假设智能卡 API 在 iOS 上可用(TKSmartCardSlotManager.default 在 iOS 上为 nil,应做可用性守卫)。不要:跳过卡片通信的会话管理(应使用 withSession 或 beginSession/endSession)。不要:忽略 APDU 响应中的状态字(应检查状态字,如 0x9000)。不要:硬编码笼统的算法支持(supports 方法必须反映硬件实际支持的能力,无条件返回 true 会导致系统尝试不支持操作时运行时失败)。
十一、评审清单 包括:验证平台可用性(TKSmartCard 仅 macOS,TKTokenWatcher iOS 14+);令牌扩展目标使用 NSExtensionPointIdentifier = com.apple.ctk-tokens;Info.plist 中 com.apple.ctk.driver-class 设置正确驱动类;安装期间通过 _securityagent 启动注册扩展;TKTokenSessionDelegate 检查具体算法而非笼统 true;打开并关闭智能卡会话(withSession 或 beginSession/endSession);每次 send 后检查 APDU 状态字;钥匙串查询前通过 TKTokenWatcher 验证令牌存在;处理 TKError 并给出适当用户反馈;用正确 objectID 值填充钥匙串内容;TKTokenKeychainKey 能力(canSign、canDecrypt)与硬件匹配;为部署环境正确配置证书信任级别;令牌移除时对持久引用处理 errSecItemNotFound。
十二、参考 扩展模式(PIV 命令、TLV 解析、通用令牌驱动、APDU 辅助、安全 PIN)见 references/cryptotokenkit-patterns.md。