跳转至

gerege-token-kit —— FEITIAN USB 令牌 SPM 包

gerege-token-kit 是一个 Swift Package Manager 包,用于本地(离线、直接插接) 与 FEITIAN USB 令牌(智能卡形态的加密设备)交互。它提供在令牌上生成密钥、 使用 PIN 登录、生成 ECDSA/RSA 签名、读写证书以及生成 PKCS#10 CSR 的能力。

无第三方依赖。 完全不使用任何第三方包 —— 仅依赖 Apple 的系统框架 (FoundationCryptoTokenKitCommonCryptoos)。源码: desktop/gerege-token-kit/Package.swift

仅限本地。 本包只与已插入的 USB 令牌直接交互 —— 不会调用 RP-API, 也不发起任何网络请求。将令牌上生成的 CSR / 签名送达服务器,是调用方应用 (例如 macOS 桌面端)的职责。

1. 功能概述

本包提供两个访问层级:

层级 类型 方式 令牌
标准(PKCS#11) PKCS11Module 通过 dlopen 加载 FEITIAN Castle 中间件(libcastle*.dylib)并调用 C_* 函数 FEITIAN Castle FTSmartToken
底层(APDU) BioPassDriver 直接发送 ISO 7816-4 APDU,在完成 mutual-auth 后使用 Secure Messaging(3DES) BioPass2003 / ePass2003(EnterSafe-FIPS 小程序)

其他配套组件:

  • TokenManager —— 基于 CryptoTokenKitTKSmartCard)的读卡器检测与会话管理。
  • APDUCommand / APDUResponse / APDUTransceiver —— APDU 构造与传输引擎。
  • SecureMessaging —— ePass2003 的 3DES 安全通道(mutual auth、APDU 封装 / 解封装)。
  • CSR —— PKCS#10 CSR(EC P-256 + ECDSA-SHA256)构造器。
  • TokenError —— 错误文案为蒙古语的 enum

2. 安装

通过本地路径引入(该包与主仓库一同发布):

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

随后链接到您的 target:

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

引入包:

import GeregeTokenKit

包信息(Package.swift):

  • 名称 / 产品:GeregeTokenKit
  • 平台:macOS 14+、iOS 17+(由于 PKCS#11 的 dlopen 路径需要真实的 USB 中间件, 实际上只在 macOS 上可用 —— 详见下文 §5)。
  • swift-tools-version:5.9

3. 公开 API

PKCS11Module(标准路径)

源码:Sources/GeregeTokenKit/PKCS11Module.swift

默认中间件路径(defaultLibraryPaths): /usr/local/lib/libcastle.1.0.0.dylib/usr/local/lib/libcastle.dylib/Library/OpenSC/lib/opensc-pkcs11.so

底层函数:

函数 作用
static open(libraryPath:) 通过 dlopen 加载 .dylib/.so 并查找 C_* 符号
initialize() / finalize() C_Initialize / C_Finalize
getSlotList(tokenPresent:) 已插入令牌的插槽 ID
openSession(slotId:readWrite:) / closeSession(_:) 打开 / 关闭会话
login(session:pin:userType:) / logout(_:) C_LoginCKU_USER/CKU_SO)/ C_Logout
findPrivateKey(session:label:) / findPublicKey(session:label:) 按标签查找密钥
signECDSA(session:privateKey:hash:) ECDSA 签名 —— 返回 r‖s
readECPoint(session:publicKey:) CKA_EC_POINT 读取原始 EC 点
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:) 通过 C_CreateObject 写入 X.509 证书
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:) 使用 SO PIN 解锁被锁定的 User PIN

BioPassDriver(底层 / APDU 路径)

源码:Sources/GeregeTokenKit/BioPassDriver.swift。 直接操作 TKSmartCard(OpenSC card-epass2003.c 的 Swift 移植)。主要函数:

函数 作用
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:) 完整初始化令牌(擦除 + 传输密钥 + 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:) —— 依据 65 字节的未压缩 EC 点 与一个签名闭包,返回 PKCS#10 CSR(DER + PEM)。

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 通过 dlopen 加载原生 .dylib 中间件,因此实际使用场景为 macOS。虽然 Package.swift 同时声明了 iOS 17,但 iOS 上没有真实的 USB 令牌与中间件, PKCS#11 路径无法工作。
  • 支持的令牌:
  • PKCS#11:FEITIAN Castle FTSmartToken(libcastle.1.0.0.dylib);默认路径中也包含 OpenSC 的 opensc-pkcs11.so
  • APDU:BioPass2003 / ePass2003(EnterSafe-FIPS 小程序)。
  • 密码学: 主路径为 EC P-256(ECDSA-SHA256);BioPassDriver/PKCS11Module 支持 RSA, 但 CSR 构造器仅支持 P-256。
  • provisioning 具有破坏性。 fullProvision / initToken / initializePIN 会清除令牌上的所有密钥与证书。

6. 与 macOS 桌面应用的关系

本包被 macOS 桌面应用(eID Mongolia 的 macOS RP 客户端)使用: 该应用在自身的 Core/Token/TokenManager.swift 中封装了 GeregeTokenKit.TokenManager.sharedBioPassDriver, 并实现了使用 USB 令牌签署 PDF 的实验性流程。

7. 链接