
cryptotokenkit
熱門使用 CryptoTokenKit 存取安全令牌(Security Token)与智慧卡(Smart Card)。适用于建置 TKTokenDriver 或 TKSmartCardTokenDriver 扩展、透过 TKSmartCard/TKSmartCardSlotManager 与智慧卡通讯、使用 iOS 26+ NFC 智慧卡工作阶段(Sessions)、注册智慧卡、利用 kSecAttrTokenID 查询由 Token 支持的 Keychain 项目、监视 TKTokenWatcher,以及设定基于凭证的智慧卡身份验证。
使用 CryptoTokenKit 存取安全令牌(Security Token)与智慧卡(Smart Card)。适用于建置 TKTokenDriver 或 TKSmartCardTokenDriver 扩展、透过 TKSmartCard/TKSmartCardSlotManager 与智慧卡通讯、使用 iOS 26+ NFC 智慧卡工作阶段(Sessions)、注册智慧卡、利用 kSecAttrTokenID 查询由 Token 支持的 Keychain 项目、监视 TKTokenWatcher,以及设定基于凭证的智慧卡身份验证。
CryptoTokenKit
使用 CryptoTokenKit 可以在 Swift 6.3 应用程式中实现 Token 驱动程式扩展、智慧卡通讯、Token 工作阶段、由 Token 支持的 Keychain 整合,以及基于凭证的身份验证。
平台可用性: CryptoTokenKit 类别适用于 Apple 各平台,但功能支援取决于扩展点(Extension point)、Entitlement 宣告、硬体与 OS 版本。用于登入/Keychain 解锁的智慧卡 App 扩展流程仅支援 macOS。除非已启用智慧卡存取权限,否则 TKSmartCardSlotManager.default 为可选属性且会回传 nil。iOS/iPadOS 26+ 增添了 NFC 智慧卡插槽与注册功能。
目录
架构概览
CryptoTokenKit 可在硬体安全令牌(智慧卡、USB Token)与身份验证及 Keychain 服务之间架起桥梁。此框架主要有三种使用模式:
智慧卡 Token 扩展 -- macOS App 扩展,可将硬体 Token 的加密项目开放给系统登入与 Keychain 解锁使用。驱动程式负责处理 Token 的生命周期、工作阶段管理与密码学运算。
客户端 Token 存取 -- App 查询由 Token 支持的 Keychain 项目。当 Token 存在时,CryptoTokenKit 会将 Token 项目显示为标准 Keychain 条目。
NFC 智慧卡存取 -- iOS/iPadOS 26+ App 可建立临时的 NFC 智慧卡插槽,并透过 TKSmartCard 与读取到的感应式卡片进行通讯。
边界分流: 本 Skill 负责 Token/智慧卡工作阶段、由 Token 支持的 Keychain 项目与基于凭证的智慧卡身份验证。请将 Passkey/WebAuthn 与帐号登入分流至 authentication;将 Secure Enclave、CryptoKit 原语、Keychain 架构、凭证固定(Certificate Pinning)及信任策略(Trust Policy)分流至 swift-security。
关键类型
| 类型 | 角色 | 平台 |
|---|---|---|
TKTokenDriver / TKToken / TKTokenSession |
Token 驱动程式、Token 与工作阶段原语 | iOS 10+, macOS 10.12+ |
TKSmartCardTokenDriver |
智慧卡 Token 扩展的进入点 | iOS 10+, macOS 10.12+;macOS 扩展流程 |
TKSmartCard / TKSmartCardSlotManager |
底层 APDU 通讯与插槽侦测 | iOS 9+, macOS 10.10+;default 为可选属性 |
TKTokenWatcher |
监视 Token 的插入与移除 | iOS 10+, macOS 10.12+ |
TKSmartCardSlotNFCSession |
基于 NFC 的智慧卡插槽工作阶段 | iOS/iPadOS 26+ |
TKSmartCardTokenRegistrationManager |
注册 NFC 智慧卡以供日后 Keychain 使用 | iOS/iPadOS 26+ |
Token 扩展
在 macOS 上进行系统登入与 Keychain 解锁时,Token 驱动程式是一种 App 扩展,能将硬体 Token 的密码学功能提供给系统使用。主应用程式(Host App)仅作为交付该扩展的载体。
智慧卡 Token 扩展包含三个核心类别:
- TokenDriver(
TKSmartCardTokenDriver的子类别)-- 进入点 - Token(
TKSmartCardToken的子类别)-- 代表 Token 本身 - TokenSession(
TKSmartCardTokenSession的子类别)-- 处理相关运算
驱动程式类别
import CryptoTokenKit
final class TokenDriver: TKSmartCardTokenDriver, TKSmartCardTokenDriverDelegate {
func tokenDriver(
_ driver: TKSmartCardTokenDriver,
createTokenFor smartCard: TKSmartCard,
aid: Data?
) throws -> TKSmartCardToken {
return try Token(
smartCard: smartCard,
aid: aid,
instanceID: "com.example.token:\(smartCard.slot.name)",
tokenDriver: driver
)
}
}
Token 类别
Token 类别从硬体读取凭证与金钥,并填入其 Keychain 内容中:
final class Token: TKSmartCardToken, TKTokenDelegate {
init(
smartCard: TKSmartCard, aid: Data?,
instanceID: String, tokenDriver: TKSmartCardTokenDriver
) throws {
try super.init(
smartCard: smartCard, aid: aid,
instanceID: instanceID, tokenDriver: tokenDriver
)
self.delegate = self
let certData = try readCertificate(from: smartCard)
guard let cert = SecCertificateCreateWithData(nil, certData as CFData) else {
throw TKError(.corruptedData)
}
let certItem = TKTokenKeychainCertificate(certificate: cert, objectID: "cert-auth")
let keyItem = TKTokenKeychainKey(certificate: cert, objectID: "key-auth")
keyItem?.canSign = true
keyItem?.canDecrypt = false
keyItem?.isSuitableForLogin = true
self.keychainContents?.fill(with: [certItem!, keyItem!])
}
func createSession(_ token: TKToken) throws -> TKTokenSession {
TokenSession(token: token)
}
}
Info.plist 与注册
扩展的 Info.plist 必须指定驱动程式类别的名称:
NSExtension
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
Token 工作阶段
TKTokenSession 负责管理身份验证状态,并透過其 Delegate 执行密码学运算。
final class TokenSession: TKSmartCardTokenSession, TKTokenSessionDelegate {
func tokenSession(
_ session: TKTokenSession,
supports operation: TKTokenOperation,
keyObjectID: TKToken.ObjectID,
algorithm: TKTokenKeyAlgorithm
) -> Bool {
switch operation {
case .signData:
return algorithm.isAlgorithm(.rsaSignatureDigestPKCS1v15SHA256)
|| algorithm.isAlgorithm(.ecdsaSignatureDigestX962SHA256)
case .decryptData:
return algorithm.isAlgorithm(.rsaEncryptionOAEPSHA256)
case .performKeyExchange:
return algorithm.isAlgorithm(.ecdhKeyExchangeStandard)
default:
return false
}
}
func tokenSession(
_ session: TKTokenSession,
sign dataToSign: Data,
keyObjectID: TKToken.ObjectID,
algorithm: TKTokenKeyAlgorithm
) throws -> Data {
let smartCard = try getSmartCard()
return try smartCard.withSession {
try performCardSign(smartCard: smartCard, data: dataToSign, keyID: keyObjectID)
}
}
func tokenSession(
_ session: TKTokenSession,
decrypt ciphertext: Data,
keyObjectID: TKToken.ObjectID,
algorithm: TKTokenKeyAlgorithm
) throws -> Data {
let smartCard = try getSmartCard()
return try smartCard.withSession {
try performCardDecrypt(smartCard: smartCard, data: ciphertext, keyID: keyObjectID)
}
}
}
PIN 身份验证
从 beginAuthFor: 回传 TKTokenAuthOperation,以便在执行密码学运算前提示使用者输入 PIN 码:
func tokenSession(
_ session: TKTokenSession,
beginAuthFor operation: TKTokenOperation,
constraint: Any
) throws -> TKTokenAuthOperation {
let pinAuth = TKTokenSmartCardPINAuthOperation()
pinAuth.pinFormat.charset = .numeric
pinAuth.pinFormat.minPINLength = 4
pinAuth.pinFormat.maxPINLength = 8
pinAuth.smartCard = (session as? TKSmartCardTokenSession)?.smartCard
pinAuth.apduTemplate = buildVerifyAPDU()
pinAuth.pinByteOffset = 5
return pinAuth
}
智慧卡通讯
TKSmartCard 提供与智慧卡进行底层 APDU 通讯的功能。TKSmartCardSlotManager.default 为可选属性;若回传 nil 应视为硬体不可用、缺少 Entitlement/存取权限,或是执行阶段功能不受支援。
侦测读卡机
import CryptoTokenKit
func discoverSmartCards() {
guard let slotManager = TKSmartCardSlotManager.default else {
print("Smart card services unavailable")
return
}
for slotName in slotManager.slotNames {
slotManager.getSlot(withName: slotName) { slot in
guard let slot else { return }
if slot.state == .validCard, let card = slot.makeSmartCard() {
communicateWith(card: card)
}
}
}
}
发送 APDU 指令
使用 send(ins:p1:p2:data:le:) 执行结构化的 APDU 通讯。请务必将呼叫包覆在 withSession 中:
func selectApplication(card: TKSmartCard, aid: Data) throws {
try card.withSession {
let (sw, response) = try card.send(
ins: 0xA4, p1: 0x04, p2: 0x00, data: aid, le: nil
)
guard sw == 0x9000 else {
throw TKError(.communicationError)
}
}
}
若需传输原始 APDU 位元组或非标准格式,请使用 transmit(_:reply:) 并手动管理 beginSession/endSession 生命周期。
NFC 智慧卡工作阶段(iOS/iPadOS 26+)
在 iOS/iPadOS 26+ 上,在呼叫 createNFCSlot(message:completion:) 与感应卡通讯之前,请先使用 isNFCSupported() 进行保护检查:
@available(iOS 26.0, iPadOS 26.0, *)
func readNFCSmartCard() {
guard let slotManager = TKSmartCardSlotManager.default,
slotManager.isNFCSupported() else { return }
slotManager.createNFCSlot(message: "Hold card near iPhone") { session, error in
guard let session else {
handleNFCError(error)
return
}
defer { session.end() }
guard let slotName = session.slotName,
let slot = slotManager.slotNamed(slotName),
let card = slot.makeSmartCard() else { return }
// Communicate with the NFC card using card.send(...)
}
}
Keychain 整合
当插入/存在 Token 时,CryptoTokenKit 会将其项目暴露为标准 Keychain 条目。可使用 kSecAttrTokenID 属性进行查询:
import Security
func findTokenKey(tokenID: String) throws -> SecKey {
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrTokenID as String: tokenID,
kSecReturnRef as String: true
]
var result: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let key = result else {
throw TKError(.objectNotFound)
}
return key as! SecKey
}
使用 kSecReturnPersistentRef 取代 kSecReturnRef 来取得跨 App 启动依然有效的持久引用(Persistent reference)。当 Token 被拔出/移除时,该引用会失效 -- 请藉由提示使用者重新插入 Token 来处理 errSecItemNotFound。
使用 kSecClass: kSecClassCertificate 也可以用同样的方式查询凭证。
凭证身份验证
Token 金钥需求
为了进行使用者登入,Token 必须包含至少一把支援以下签名算法的金钥:EC 签名杂凑 X962、RSA 签名杂凑 PSS 或 RSA 签名杂凑 PKCS1v15。
若要解密/解锁 Keychain,Token 需要:
- 支援
ecdhKeyExchangeStandard的 256 位元 EC 金钥(kSecAttrKeyTypeECSECPrimeRandom),或 - 支援
rsaEncryptionOAEPSHA256解密的 2048/3072/4096 位元 RSA 金钥(kSecAttrKeyTypeRSA)
智慧卡身份验证偏好设定(macOS)
于 com.apple.security.smartcard 网域中设定(透过 MDM 或全系统级):
| Key | Default | Descripti



