跳到主要内容
知仓学习社ZHICANG

cryptotokenkit

Access security tokens and smart cards using CryptoTokenKit. Use when building TKTokenDriver or TKSmartCardTokenDriver extensions, communicating wit…

不碰外部(只输出文字)无严重或高危命中dpearson2699/swift-ios-skills

它会碰到什么

扫了多少3 个文本文件,43 KB
它会碰到什么不碰外部(只输出文字)
命中总数0 处
命中统计严重 0 · 高 0 · 中 0 · 低 0

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

CryptoTokenKit

Use CryptoTokenKit for token driver extensions, smart-card communication,

token sessions, token-backed keychain integration, and certificate-based

authentication in Swift 6.3 apps.

Platform availability: CryptoTokenKit classes are available across Apple

platforms, but capability depends on extension point, entitlement, hardware, and

OS version. The smart-card app extension flow for login/keychain unlock is macOS.

TKSmartCardSlotManager.default is optional and returns nil unless smart-card

access is enabled. iOS/iPadOS 26+ add NFC smart-card slots and registration.

Contents

  • [Architecture Overview](#architecture-overview)
  • [Token Extensions](#token-extensions)
  • [Token Sessions](#token-sessions)
  • [Smart Card Communication](#smart-card-communication)
  • [Keychain Integration](#keychain-integration)
  • [Certificate Authentication](#certificate-authentication)
  • [Token Watching](#token-watching)
  • [Error Handling](#error-handling)
  • [Common Mistakes](#common-mistakes)
  • [Review Checklist](#review-checklist)
  • [References](#references)

Architecture Overview

CryptoTokenKit bridges hardware security tokens (smart cards, USB tokens)

with authentication and keychain services. The framework has three main usage

modes:

Smart-card token extensions -- macOS app extensions that make a hardware

token's cryptographic items available to system login and keychain unlock. The

driver handles token lifecycle, session management, and cryptographic operations.

Client-side token access -- Apps query the keychain for items backed by

tokens. CryptoTokenKit exposes token items as standard keychain entries when a

token is present.

NFC smart-card access -- iOS/iPadOS 26+ apps create a temporary NFC smart

card slot and communicate with the presented contactless card through

TKSmartCard.

Boundary routing: Own token/smart-card sessions, token-backed keychain

items, and certificate-based smart-card auth. Route passkeys/WebAuthn and

account sign-in to authentication; route Secure Enclave, CryptoKit primitives,

keychain architecture, certificate pinning, and trust policy to swift-security.

Key Types

| Type | Role | Platform |

|---|---|---|

| TKTokenDriver / TKToken / TKTokenSession | Token driver, token, and session primitives | iOS 10+, macOS 10.12+ |

| TKSmartCardTokenDriver | Entry point for smart card token extensions | iOS 10+, macOS 10.12+; macOS extension flow |

| TKSmartCard / TKSmartCardSlotManager | Low-level APDU communication and slot discovery | iOS 9+, macOS 10.10+; default is optional |

| TKTokenWatcher | Observes token insertion and removal | iOS 10+, macOS 10.12+ |

| TKSmartCardSlotNFCSession | NFC-backed smart card slot session | iOS/iPadOS 26+ |

| TKSmartCardTokenRegistrationManager | Registers NFC smart cards for later keychain use | iOS/iPadOS 26+ |

Token Extensions

For system login and keychain unlock on macOS, a token driver is an app

extension that makes a hardware token's cryptographic capabilities available to

the system. The host app exists only as a delivery mechanism for the extension.

A smart card token extension has three core classes:

  1. TokenDriver (subclass of TKSmartCardTokenDriver) -- entry point
  2. Token (subclass of TKSmartCardToken) -- represents the token
  3. TokenSession (subclass of TKSmartCardTokenSession) -- handles operations

Driver Class

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 Class

The token reads certificates and keys from hardware and populates its

keychain contents:

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 and Registration

The extension's Info.plist must name the driver class:

NSExtension
  NSExtensionAttributes
    com.apple.ctk.driver-class = $(PRODUCT_MODULE_NAME).TokenDriver
  NSExtensionPointIdentifier = com.apple.ctk-tokens

Register the extension once by launching the host app as _securityagent:

sudo -u _securityagent /Applications/TokenHost.app/Contents/MacOS/TokenHost

Token Sessions

TKTokenSession manages authentication state and performs cryptographic

operations via its 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 Authentication

Return a TKTokenAuthOperation from beginAuthFor: to prompt the user

for PIN entry before cryptographic operations:

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
}

Smart Card Communication

TKSmartCard provides low-level APDU communication with smart cards.

TKSmartCardSlotManager.default is optional; treat nil as unavailable

hardware, missing entitlement/access, or unsupported runtime capability.

Discovering Card Readers

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)
            }
        }
    }
}

Sending APDU Commands

Use send(ins:p1:p2:data:le:) for structured APDU communication.

Always wrap calls in 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)
        }
    }
}

For raw APDU bytes or non-standard formats, use transmit(_:reply:) with

manual beginSession/endSession lifecycle management.

NFC Smart Card Sessions (iOS/iPadOS 26+)

On iOS/iPadOS 26+, guard isNFCSupported() before calling

createNFCSlot(message:completion:) to communicate with contactless cards:

@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 Integration

When a token is present, CryptoTokenKit exposes its items as standard

keychain entries. Query them using the kSecAttrTokenID attribute:

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
}

Use kSecReturnPersistentRef instead of kSecReturnRef to obtain a

persistent reference that survives across app launches. The reference

becomes invalid when the token is removed -- handle errSecItemNotFound

by prompting the user to reinsert the token.

Query certificates the same way with kSecClass: kSecClassCertificate.

Certificate Authentication

Token Key Requirements

For user login, the token must contain at least one key capable of signing

with: EC signature digest X962, RSA signature digest PSS, or RSA signature

digest PKCS1v15.

For keychain unlock, the token needs:

  • 256-bit EC key (kSecAttrKeyTypeECSECPrimeRandom) supporting

ecdhKeyExchangeStandard, or

  • 2048/3072/4096-bit RSA key (kSecAttrKeyTypeRSA) supporting

rsaEncryptionOAEPSHA256 decryption

Smart Card Authentication Preferences (macOS)

Configure in the com.apple.security.smartcard domain (MDM or systemwide):

| Key | Default | Description |

|---|---|---|

| allowSmartCard | true | Enable smart card authentication |

| checkCertificateTrust | 0 | Certificate trust level (0-3) |

| oneCardPerUser | false | Pair a single smart card to an account |

| enforceSmartCard | false | Require smart card for login |

Trust levels: 0 = trust all, 1 = validity + issuer, 2 = + soft

revocation, 3 = + hard revocation.

Token Watching

TKTokenWatcher monitors token insertion and removal. Available on iOS 10+

and macOS 10.12+. Enumerate tokenIDs, install an insertion handler, then add a

removal handler for each observed token. Keep the watcher alive for as long as

monitoring is required. For slot-level reader state, use

[Smart Card Slot Monitoring](references/cryptotokenkit-patterns.md#smart-card-slot-monitoring).

Error Handling

CryptoTokenKit operations throw TKError. Key error codes:

| Code | Meaning |

|---|---|

| .notImplemented | Operation not supported by this token |

| .communicationError | Communication with token failed |

| .corruptedData | Data from token is corrupted |

| .canceledByUser | User canceled the operation |

| .authenticationFailed | PIN or password incorrect |

| .objectNotFound | Requested key or certificate not found |

| .tokenNotFound | Token is no longer present |

| .authenticationNeeded | Authentication required before operation |

Common Mistakes

DON'T: Query token keychain items without checking token presence

// WRONG -- query may fail if token was removed
let key = try findTokenKey(tokenID: savedTokenID)

// CORRECT -- verify the token is still present first
let watcher = TKTokenWatcher()
guard watcher.tokenIDs.contains(savedTokenID) else {
    promptUserToInsertToken()
    return
}
let key = try findTokenKey(tokenID: savedTokenID)

DON'T: Treat API availability as an access guarantee

// WRONG -- may be nil without entitlement, hardware, or runtime support
let manager = TKSmartCardSlotManager.default!  // Crashes when unavailable

// CORRECT -- guard availability/access before using smart card slots
guard let manager = TKSmartCardSlotManager.default else {
    print("Smart card services unavailable")
    return
}

DON'T: Skip session management for card communication

// WRONG -- sending commands without a session
card.transmit(apdu) { response, error in /* may fail */ }

// CORRECT -- use withSession or beginSession/endSession
try card.withSession {
    let (sw, response) = try card.send(
        ins: 0xCA, p1: 0x00, p2: 0x6E, data: nil, le: 0
    )
}

DON'T: Ignore status words in APDU responses

// WRONG -- assuming success
let (_, response) = try card.send(ins: 0xA4, p1: 0x04, p2: 0x00, data: aid, le: nil)

// CORRECT -- check status word
let (sw, response) = try card.send(ins: 0xA4, p1: 0x04, p2: 0x00, data: aid, le: nil)
guard sw == 0x9000 else {
    throw SmartCardError.commandFailed(statusWord: sw)
}

DON'T: Hard-code blanket algorithm support

The supports delegate method must reflect what the hardware actually

implements. Returning true unconditionally causes runtime failures when

the system attempts unsupported operations.

Review Checklist

  • [ ] Platform availability verified for the exact capability (TKTokenWatcher iOS 10+, NFC smart-card sessions iOS/iPadOS 26+)
  • [ ] TKSmartCardSlotManager.default guarded for missing entitlement, hardware, or runtime support
  • [ ] macOS token extension target uses NSExtensionPointIdentifier = com.apple.ctk-tokens
  • [ ] com.apple.ctk.driver-class set to the correct driver class in Info.plist
  • [ ] Extension registered via _securityagent launch during installation
  • [ ] TKTokenSessionDelegate checks specific algorithms, not blanket true
  • [ ] Smart card sessions opened and closed (withSession or beginSession/endSession)
  • [ ] APDU status words checked after every send call
  • [ ] Token presence verified via TKTokenWatcher before keychain queries
  • [ ] TKError cases handled with appropriate user feedback
  • [ ] Keychain contents populated with correct objectID values
  • [ ] TKTokenKeychainKey capabilities (canSign, canDecrypt) match hardware
  • [ ] Certificate trust level configured appropriately for deployment environment
  • [ ] errSecItemNotFound handled for persistent references when token is removed
  • [ ] iOS 26+ NFC sessions ended with TKSmartCardSlotNFCSession.end()

References

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。