Перейти к содержанию

gerege-token-kit — SPM-пакет для USB-токенов FEITIAN

gerege-token-kit — пакет Swift Package Manager для локального взаимодействия (офлайн, напрямую с подключённым устройством) с USB-токенами FEITIAN (криптоустройствами в форм-факторе смарт-карты). Он позволяет генерировать ключи на токене, входить по PIN, создавать подписи ECDSA/RSA, читать и записывать сертификаты и формировать PKCS#10 CSR.

Без зависимостей. Пакет вообще не использует сторонние библиотеки — только системные фреймворки Apple (Foundation, CryptoTokenKit, CommonCrypto, os). Исходник: desktop/gerege-token-kit/Package.swift.

Только локально. Пакет работает исключительно с подключённым USB-токеном — он не делает ни вызовов RP-API, ни каких-либо сетевых запросов. Доставка созданных на токене CSR/подписи на сервер — задача вызывающего приложения (например, десктопа для macOS).

1. Что он умеет

Пакет предоставляет два уровня доступа:

Уровень Тип Метод Токен
Стандартный (PKCS#11) PKCS11Module Загружает middleware FEITIAN Castle (libcastle*.dylib) через dlopen и вызывает функции C_* FEITIAN Castle FTSmartToken
Низкоуровневый (APDU) BioPassDriver Отправляет APDU ISO 7816-4 напрямую и после mutual-auth использует Secure Messaging (3DES) BioPass2003 / ePass2003 (апплет EnterSafe-FIPS)

Дополнительные вспомогательные компоненты:

  • TokenManager — обнаружение считывателей и управление сессиями на базе CryptoTokenKit (TKSmartCard).
  • APDUCommand / APDUResponse / APDUTransceiver — движок сборки и передачи APDU.
  • SecureMessaging — защищённый канал 3DES для ePass2003 (mutual auth, wrap/unwrap APDU).
  • CSR — построитель PKCS#10 CSR (EC P-256 + ECDSA-SHA256).
  • TokenErrorenum с текстами ошибок на монгольском языке.

2. Установка

Подключается по локальному пути (пакет поставляется в том же репозитории):

// Package.swift
dependencies: [
    .package(path: "../gerege-token-kit")
]

Затем подключите его к своему таргету:

.target(name: "MyApp", dependencies: [
    .product(name: "GeregeTokenKit", targets: ["GeregeTokenKit"])
])

Импорт пакета:

import GeregeTokenKit

Информация о пакете (Package.swift):

  • Имя / продукт: GeregeTokenKit
  • Платформы: macOS 14+, iOS 17+ (поскольку путь PKCS#11 через dlopen требует настоящего USB-middleware, на практике он работает на macOS — см. §5 ниже).
  • swift-tools-version: 5.9

3. Публичный API

PKCS11Module (стандартный путь)

Исходник: Sources/GeregeTokenKit/PKCS11Module.swift.

Пути к middleware по умолчанию (defaultLibraryPaths): /usr/local/lib/libcastle.1.0.0.dylib, /usr/local/lib/libcastle.dylib, /Library/OpenSC/lib/opensc-pkcs11.so.

Низкоуровневые функции:

Функция Назначение
static open(libraryPath:) Загружает .dylib/.so через dlopen и находит символы C_*
initialize() / finalize() C_Initialize / C_Finalize
getSlotList(tokenPresent:) ID слотов с подключённым токеном
openSession(slotId:readWrite:) / closeSession(_:) Открыть/закрыть сессию
login(session:pin:userType:) / logout(_:) C_Login (CKU_USER/CKU_SO) / C_Logout
findPrivateKey(session:label:) / findPublicKey(session:label:) Поиск ключа по метке
signECDSA(session:privateKey:hash:) Подпись ECDSA — возвращает r‖s
readECPoint(session:publicKey:) Чтение сырой точки EC из CKA_EC_POINT
initToken(slotId:soPIN:label:) C_InitToken — сброс к заводским настройкам + SO PIN
initPIN(session:userPIN:) / setPIN(session:oldPIN:newPIN:) Задать/сменить пользовательский PIN
generateECKeyPair(session:label:keyID:) Генерация пары ключей EC P-256 на токене
writeCertificate(session:certificateDER:label:keyID:subjectDER:) Запись сертификата X.509 через C_CreateObject
listObjects(session:) Список всех объектов токена в виде TokenObjectInfo
destroyObject(session:handle:) Удаление объекта (C_DestroyObject)

Высокоуровневые вспомогательные функции (сами выбирают слот, выполняют вход и выход, сессия закрывается автоматически):

Функция Назначение
signECDSA(pin:keyLabel:hash:) Вход по PIN, поиск ключа по метке и подписание
generateCSR(pin:keyLabel:subject:) Формирование PKCS#10 CSR (DER+PEM) с ключом на токене
fullProvision(soPIN:userPIN:label:keyLabel:keyID:) Сброс + SO/User PIN + пара ключей EC (⚠ все предыдущие ключи стираются)
listObjects(pin:) Вход и вывод списка всех объектов
importCertificate(pin:label:certificateDER:keyID:) Импорт сертификата
deleteObject(pin:idHex:kind:) Удаление объекта по idHex+kind
generateSigningKey(pin:label:keyID:) Генерация пары ключей подписи EC P-256
changeUserPIN(oldPIN:newPIN:) / changeSOPIN(oldPIN:newPIN:) Смена PIN
unlockUserPIN(soPIN:newUserPIN:) Разблокировка заблокированного User PIN с помощью SO PIN

BioPassDriver (низкоуровневый путь / APDU)

Исходник: Sources/GeregeTokenKit/BioPassDriver.swift. Работает напрямую с TKSmartCard (Swift-порт card-epass2003.c из OpenSC). Основные функции:

Функция Назначение
selectApplet(card:) Выбор апплета EnterSafe-FIPS
establishSecureSession(card:...) Mutual auth → установка сессии Secure Messaging
verifyPIN(_:reference:card:) Проверка User/SO PIN (при ошибке — pinVerifyFailed(retriesLeft:))
getTokenInfo(card:) ATR, метка, признак FIPS (TokenInfo)
signECDSA(hash:card:) / signRSA(data:card:) Подписание
generateECKeyPair(keyID:card:) / generateRSAKeyPair(keyID:keySize:card:) Генерация ключа (требуется SM)
readCertificate(fileID:card:) / writeCertificate(data:fileID:card:) Чтение/запись сертификата
initializePIN(pin:puk:card:) Полная инициализация токена (стирание + transport key + файловая система PKCS#15 + PIN)

TokenManager (считыватель/сессия)

Исходник: Sources/GeregeTokenKit/TokenManager.swift.

  • TokenManager.shared — синглтон
  • getReaderNames() -> [String], hasToken() -> Bool
  • withSession(readerIndex:_:) — открывает сессию, выполняет операцию и закрывает автоматически
  • getATR(readerIndex:)

CSR

Исходник: Sources/GeregeTokenKit/CSR.swift.

  • CSR.Subject(commonName:organization:country:email:)
  • CSR.buildP256(subject:publicKeyPoint:signer:) — возвращает PKCS#10 CSR (DER + PEM) из 65-байтовой несжатой точки EC и замыкания-подписанта.

4. Примеры использования

Проверить наличие считывателя

import GeregeTokenKit

let mgr = TokenManager.shared
if mgr.hasToken() {
    print("Reader-ууд:", mgr.getReaderNames())
    let atr = try await mgr.getATR()
    print("ATR:", bytesToHex(atr))
}

Подписание через PKCS#11 (высокий уровень)

let hash = /* SHA-256 digest, 32 bytes */
let p11 = try PKCS11Module.open()               // загружает libcastle*.dylib
let signature = try p11.signECDSA(              // возвращает r‖s
    pin: "12345678",
    keyLabel: "gerege",
    hash: hash
)

Сформировать CSR с ключом на токене

let p11 = try PKCS11Module.open()
let subject = CSR.Subject(commonName: "Бат-Эрдэнэ", country: "MN")
let (der, pem) = try await p11.generateCSR(
    pin: "12345678",
    keyLabel: "gerege",
    subject: subject
)
// отправьте pem в CA для получения сертификата (за пределами этого пакета)

На уровне APDU (BioPassDriver)

let driver = BioPassDriver()
try await TokenManager.shared.withSession { card in
    _ = try await driver.selectApplet(card: card)
    try await driver.establishSecureSession(card: card)   // 3DES SM
    try await driver.verifyPIN("12345678", card: card)
    let sig = try await driver.signECDSA(hash: hash, card: card)
    return sig
}

5. Ограничения

  • Только локально. Пакет ничего не отправляет в сеть — он работает лишь с подключённым токеном.
  • Платформа. PKCS11Module загружает нативный .dylib-middleware через dlopen, поэтому на практике применяется на macOS. Хотя Package.swift объявляет и iOS 17, путь PKCS#11 на iOS не заработает — там нет реального USB-токена и middleware.
  • Поддерживаемые токены:
  • PKCS#11: FEITIAN Castle FTSmartToken (libcastle.1.0.0.dylib); в пути по умолчанию включён также OpenSC opensc-pkcs11.so.
  • APDU: BioPass2003 / ePass2003 (апплет EnterSafe-FIPS).
  • Криптография: основной путь — EC P-256 (ECDSA-SHA256); RSA поддерживается BioPassDriver/PKCS11Module, но построитель CSR работает только с P-256.
  • Provisioning разрушителен. fullProvision / initToken / initializePIN стирают все ключи и сертификаты на токене.

6. Связь с десктопным приложением macOS

Этот пакет используется десктопным приложением macOS (RP-клиентом eID Mongolia для macOS): приложение оборачивает GeregeTokenKit.TokenManager.shared и BioPassDriver в собственный Core/Token/TokenManager.swift и реализует экспериментальный сценарий подписания PDF с помощью USB-токена.

7. Ссылки