iOS Security Framework 密钥导出:从 PKCS#1 到 PKCS#8 的 2 种手动封装方案
在 iOS/macOS 开发中,处理密钥格式转换是密码学操作中常见的需求。特别是当我们需要将系统 API 生成的 PKCS#1 格式密钥转换为更通用的 PKCS#8 格式时,这一过程需要开发者对 ASN.1 结构有深入理解。本文将详细介绍两种手动封装方案,帮助开发者实现跨平台密钥交换。
1. PKCS#1 与 PKCS#8 格式解析
在开始转换之前,我们需要清楚两种格式的本质区别。
PKCS#1 格式特点:
- 专为 RSA 算法设计
- 直接包含 RSA 私钥的核心参数(n, e, d, p, q 等)
- iOS 的
SecKeyCopyExternalRepresentation默认生成此格式 - 典型 PEM 文件头标记为
-----BEGIN RSA PRIVATE KEY-----
PKCS#8 格式特点:
- 支持多种非对称加密算法(RSA、ECC 等)
- 包含算法标识符和版本号
- 更通用的跨平台兼容性
- 典型 PEM 文件头标记为
-----BEGIN PRIVATE KEY-----
1.1 ASN.1 结构对比
通过 ASN.1 结构可以更直观地理解两种格式的差异:
PKCS#1 私钥结构:
RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL }PKCS#8 私钥结构:
PrivateKeyInfo ::= SEQUENCE { version Version, algorithm AlgorithmIdentifier, privateKey PrivateKey, attributes [0] IMPLICIT Attributes OPTIONAL } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }关键区别在于 PKCS#8 使用容器包装 PKCS#1 数据,并添加了算法标识信息。
2. 方案一:基础 ASN.1 手动封装
这是最直接的转换方法,适合需要完全控制 ASN.1 结构的场景。
2.1 实现步骤
- 获取 PKCS#1 格式的 DER 数据
var error: Unmanaged<CFError>? guard let pkcs1Data = SecKeyCopyExternalRepresentation(privateKey, &error) as Data? else { throw error!.takeRetainedValue() as Error }- 构建 PKCS#8 的 ASN.1 结构
// 版本号 (0) let version = Data([0x02, 0x01, 0x00]) // RSA 算法标识符 (1.2.840.113549.1.1.1) let rsaOID = Data([0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00]) // 将PKCS1数据包装为OCTET STRING let pkcs1OctetString = Data([0x04]) + derLength(for: pkcs1Data) + pkcs1Data // 拼接完整PKCS8结构 let pkcs8SequenceData = version + rsaOID + pkcs1OctetString let pkcs8Data = Data([0x30]) + derLength(for: pkcs8SequenceData) + pkcs8SequenceData- 转换为 PEM 格式
let base64Content = pkcs8Data.base64EncodedString(options: .lineLength64Characters) return "-----BEGIN PRIVATE KEY-----\n\(base64Content)\n-----END PRIVATE KEY-----"2.2 关键辅助函数
private func derLength(for data: Data) -> Data { let length = data.count switch length { case 0..<0x80: return Data([UInt8(length)]) case 0x80...0xFF: return Data([0x81, UInt8(length)]) case 0x100...0xFFFF: return Data([0x82, UInt8(length >> 8), UInt8(length & 0xFF)]) default: return Data([0x83, UInt8(length >> 16), UInt8((length >> 8) & 0xFF), UInt8(length & 0xFF)]) } }2.3 使用示例
do { let pkcs8PEM = try privateKey.convertPKCS1ToPKCS8PEM() print("转换结果:\n\(pkcs8PEM)") } catch { print("转换失败: \(error)") }3. 方案二:使用 Security Framework 高级封装
对于需要更高安全性的场景,我们可以利用 Security Framework 提供的功能进行封装。
3.1 实现步骤
- 创建临时密钥属性字典
let keyAttributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits as String: 2048, kSecAttrIsPermanent as String: false ]- 转换密钥格式
var error: Unmanaged<CFError>? guard let pkcs8Data = SecKeyCopyExternalRepresentation(privateKey, &error) as Data?, let pkcs8Key = SecKeyCreateWithData(pkcs8Data as CFData, keyAttributes as CFDictionary, &error) else { throw error!.takeRetainedValue() as Error }- 导出 PKCS#8 格式
let exportParams: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits as String: 2048, kSecAttrIsExtractable as String: true ] guard let exportedData = SecKeyCopyExternalRepresentation(pkcs8Key, &error) as Data? else { throw error!.takeRetainedValue() as Error }3.2 完整实现
func convertToPKCS8UsingSecurityFramework(privateKey: SecKey) throws -> String { // 1. 定义密钥属性 let keyAttributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits as String: 2048, kSecAttrIsPermanent as String: false ] // 2. 获取PKCS#1数据 var error: Unmanaged<CFError>? guard let pkcs1Data = SecKeyCopyExternalRepresentation(privateKey, &error) as Data? else { throw error!.takeRetainedValue() as Error } // 3. 重新创建密钥对象 guard let newKey = SecKeyCreateWithData(pkcs1Data as CFData, keyAttributes as CFDictionary, &error) else { throw error!.takeRetainedValue() as Error } // 4. 导出PKCS#8格式 let exportParams: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits as String: 2048, kSecAttrIsExtractable as String: true ] guard let pkcs8Data = SecKeyCopyExternalRepresentation(newKey, &error) as Data? else { throw error!.takeRetainedValue() as Error } // 5. 转换为PEM格式 let base64Content = pkcs8Data.base64EncodedString(options: .lineLength64Characters) return "-----BEGIN PRIVATE KEY-----\n\(base64Content)\n-----END PRIVATE KEY-----" }4. 两种方案的对比与选择
| 特性 | 方案一:手动 ASN.1 封装 | 方案二:Security Framework 封装 |
|---|---|---|
| 实现复杂度 | 高,需要了解 ASN.1 细节 | 低,使用系统 API |
| 灵活性 | 高,可完全控制结构 | 中,受限于系统实现 |
| 性能 | 较高 | 较低,涉及额外密钥操作 |
| 安全性 | 依赖开发者实现 | 使用系统加密模块 |
| 跨平台兼容性 | 需要手动调整不同平台 | 仅限 Apple 平台 |
| 维护成本 | 高 | 低 |
选择建议:
- 需要最大控制权或跨平台需求:选择方案一
- 开发仅运行在 Apple 平台的应用:选择方案二
- 对安全性要求极高的场景:推荐方案二
5. 实际应用中的注意事项
- 密钥可导出性:
// 生成密钥时必须设置可导出属性 let keyPairAttributes: [CFString: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits: 2048, kSecAttrIsExtractable: true, // 必须为true kSecAttrIsPermanent: false ]- 内存安全处理:
// 安全清除内存中的敏感数据 pkcs1Data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in memset(UnsafeMutableRawPointer(mutating: buffer.baseAddress!), 0, buffer.count) }- 性能优化技巧:
// 预计算常用ASN.1结构 private static let rsaAlgorithmID: Data = { var data = Data() data.append(0x30) // SEQUENCE data.append(0x0D) // Length // ... 完整算法标识 return data }()- 错误处理最佳实践:
do { let pkcs8Key = try convertToPKCS8(privateKey) // 使用转换后的密钥 } catch KeyConversionError.invalidKeyData { // 处理特定错误 } catch { // 处理未知错误 logError("密钥转换失败: \(error.localizedDescription)") }6. 验证转换结果
为确保转换正确性,我们可以通过 OpenSSL 验证结果:
# 验证PKCS#8格式 openssl rsa -in converted.pem -inform PEM -check # 提取公钥验证配对 openssl rsa -in converted.pem -pubout -out public.pem # 对比原始公钥和导出公钥 diff <(openssl rsa -in original.key -pubout) public.pem也可以通过代码验证:
func verifyKeyPair(privateKey: SecKey, publicKey: SecKey) -> Bool { let testData = "验证数据".data(using: .utf8)! var error: Unmanaged<CFError>? guard let encrypted = SecKeyCreateEncryptedData( publicKey, .rsaEncryptionPKCS1, testData as CFData, &error) else { return false } guard let decrypted = SecKeyCreateDecryptedData( privateKey, .rsaEncryptionPKCS1, encrypted, &error) else { return false } return testData == decrypted as Data }7. 高级话题:性能优化与安全增强
对于高频使用的场景,我们可以进一步优化:
- 缓存 ASN.1 结构:
private struct ASN1Cache { static let rsaOID: Data = { var data = Data() data.append(0x30) // SEQUENCE data.append(0x0D) // Length // ... 完整OID数据 return data }() static let version: Data = { return Data([0x02, 0x01, 0x00]) }() }- 安全内存处理扩展:
extension Data { mutating func secureErase() { withUnsafeMutableBytes { (buffer: UnsafeMutableRawBufferPointer) in memset(buffer.baseAddress!, 0, buffer.count) } } static func secureRandom(count: Int) -> Data? { var data = Data(count: count) let result = data.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, count, $0.baseAddress!) } return result == errSecSuccess ? data : nil } }- 批量处理优化:
func batchConvertKeys(keys: [SecKey]) throws -> [String] { return try keys.concurrentMap { key in try convertToPKCS8(privateKey: key) } } extension Array { func concurrentMap<T>(_ transform: (Element) throws -> T) throws -> [T] { var results = [Result<T, Error>?](repeating: nil, count: count) let queue = DispatchQueue(label: "concurrent.map.queue") DispatchQueue.concurrentPerform(iterations: count) { index in do { let result = try transform(self[index]) queue.sync { results[index] = .success(result) } } catch { queue.sync { results[index] = .failure(error) } } } return try results.map { try $0!.get() } } }8. 跨平台兼容性处理
当需要与不同平台交互时,还需要考虑以下因素:
- Java 平台兼容性:
// Java 读取PKCS#8 PEM文件 private static PrivateKey loadPrivateKey(String filename) throws Exception { String content = new String(Files.readAllBytes(Paths.get(filename))); content = content.replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] decoded = Base64.getDecoder().decode(content); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(keySpec); }- Node.js 兼容性处理:
const fs = require('fs'); const crypto = require('crypto'); function loadPrivateKey(path) { const pem = fs.readFileSync(path, 'utf8'); return crypto.createPrivateKey({ key: pem, format: 'pem', type: 'pkcs8' }); }- Python 兼容性示例:
from cryptography.hazmat.primitives import serialization def load_private_key(path): with open(path, "rb") as key_file: return serialization.load_pem_private_key( key_file.read(), password=None, )9. 密钥转换的单元测试
为确保转换可靠性,应建立完善的测试套件:
class KeyConversionTests: XCTestCase { var testPrivateKey: SecKey! override func setUp() { super.setUp() testPrivateKey = generateTestKey() } func testPKCS8Conversion() { do { let pkcs8PEM = try convertToPKCS8(privateKey: testPrivateKey) XCTAssertTrue(pkcs8PEM.hasPrefix("-----BEGIN PRIVATE KEY-----")) XCTAssertTrue(pkcs8PEM.hasSuffix("-----END PRIVATE KEY-----\n")) // 验证可以重新导入 let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate ] var error: Unmanaged<CFError>? let key = SecKeyCreateWithData( Data(pkcs8PEM.utf8) as CFData, attributes as CFDictionary, &error) XCTAssertNotNil(key) XCTAssertNil(error) } catch { XCTFail("转换失败: \(error)") } } private func generateTestKey() -> SecKey { let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits as String: 2048, kSecAttrIsExtractable as String: true ] var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { fatalError("无法生成测试密钥: \(error!.takeRetainedValue())") } return privateKey } }10. 实际项目集成建议
将密钥转换功能集成到项目时,建议采用以下模式:
- 协议抽象:
protocol KeyFormatConverter { func convertToPKCS8(privateKey: SecKey) throws -> String func convertFromPKCS8(pemString: String) throws -> SecKey } struct DefaultKeyConverter: KeyFormatConverter { // 实现协议方法 }- 依赖注入:
class CryptoService { private let keyConverter: KeyFormatConverter init(converter: KeyFormatConverter = DefaultKeyConverter()) { self.keyConverter = converter } func exportKey(privateKey: SecKey) throws -> String { return try keyConverter.convertToPKCS8(privateKey: privateKey) } }- 性能监控:
func measureConversionPerformance() { let metrics: [XCTMetric] = [XCTClockMetric(), XCTMemoryMetric()] let measureOptions = XCTMeasureOptions.default measureOptions.iterationCount = 100 measure(metrics: metrics, options: measureOptions) { do { _ = try convertToPKCS8(privateKey: testPrivateKey) } catch { XCTFail("性能测试失败: \(error)") } } }- 安全审计日志:
struct SecurityLogger { static func logKeyOperation(_ operation: String, metadata: [String: Any] = [:]) { let logData: [String: Any] = [ "timestamp": Date().timeIntervalSince1970, "operation": operation, "metadata": metadata ] // 安全地记录到文件或发送到服务器 // 注意: 实际实现中不应记录密钥内容本身 } }11. 密钥生命周期管理
完善的密钥管理策略应包括:
- 密钥生成最佳实践:
func generateSecureKeyPair() throws -> (publicKey: SecKey, privateKey: SecKey) { let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits as String: 2048, kSecAttrIsExtractable as String: true, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, // 如果可用 kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: false, kSecAttrAccessControl as String: SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], nil)! ] ] var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error), let publicKey = SecKeyCopyPublicKey(privateKey) else { throw error!.takeRetainedValue() as Error } return (publicKey, privateKey) }- 密钥存储建议:
protocol KeyStorage { func store(key: SecKey, identifier: String) throws func retrieveKey(identifier: String) throws -> SecKey? func deleteKey(identifier: String) throws } struct KeychainStorage: KeyStorage { // 实现安全的密钥存储 }- 密钥轮换策略:
class KeyRotationManager { private let keyStorage: KeyStorage private let cryptoService: CryptoService private let rotationInterval: TimeInterval init(storage: KeyStorage, service: CryptoService, rotationInterval: TimeInterval = 30 * 24 * 60 * 60) { // 默认30天 self.keyStorage = storage self.cryptoService = service self.rotationInterval = rotationInterval } func rotateIfNeeded(identifier: String) throws -> SecKey { if let existingKey = try keyStorage.retrieveKey(identifier: identifier), let creationDate = getKeyCreationDate(key: existingKey), Date().timeIntervalSince(creationDate) < rotationInterval { return existingKey } // 生成新密钥 let (publicKey, privateKey) = try generateSecureKeyPair() try keyStorage.store(key: privateKey, identifier: identifier) // 这里可以添加密钥分发逻辑 distributeNewKey(publicKey) return privateKey } private func getKeyCreationDate(key: SecKey) -> Date? { let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecValueRef as String: key, kSecReturnAttributes as String: true ] var result: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, let attributes = result as? [String: Any], let creationDate = attributes[kSecAttrCreationDate as String] as? Date else { return nil } return creationDate } }12. 故障排查与常见问题
开发过程中可能遇到的典型问题及解决方案:
- 密钥不可导出错误:
错误信息:
Error Domain=NSOSStatusErrorDomain Code=-25300 "Item not exportable"
解决方案:
// 确保生成密钥时设置了kSecAttrIsExtractable为true let keyAttributes: [String: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits: 2048, kSecAttrIsExtractable: true // 关键设置 ]- ASN.1 结构错误:
错误信息:
ASN1 corrupted data
解决方案:
// 验证DER长度编码是否正确 func validateDERLength(data: Data) -> Bool { guard !data.isEmpty else { return false } let firstByte = data[0] if firstByte < 0x80 { return data.count == 1 } let lengthOfLength = Int(firstByte & 0x7F) guard data.count >= 1 + lengthOfLength else { return false } // 验证长度值是否匹配实际数据长度 return true }- 跨平台兼容性问题:
Java 报错:
InvalidKeySpecException
解决方案:
// 确保PKCS#8格式符合标准,特别是版本号和算法标识 let standardPKCS8Header = Data([ 0x30, 0x82, 0x04, 0xA3, // SEQUENCE 0x02, 0x01, 0x00, // INTEGER (version = 0) 0x30, 0x0D, // SEQUENCE (algorithm) 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, // OID 0x05, 0x00, // NULL 0x04, 0x82, 0x04, 0x8B // OCTET STRING ])- 性能优化检查表:
- [ ] 预计算静态 ASN.1 结构
- [ ] 使用安全且高效的内存操作
- [ ] 实现批量处理并发机制
- [ ] 避免不必要的密钥复制操作
- [ ] 使用合适的缓冲区大小
- 安全审计要点:
- [ ] 确保敏感数据及时从内存清除
- [ ] 验证所有输入数据的有效性
- [ ] 实现完善的错误处理
- [ ] 记录关键安全事件
- [ ] 定期更新加密算法参数
13. 进阶话题:支持其他密钥类型
上述方案主要针对 RSA 密钥,但 PKCS#8 支持多种密钥类型。以下是扩展支持的方法:
- ECC 密钥支持:
func convertECKeyToPKCS8(privateKey: SecKey) throws -> String { // 获取原始密钥数据 var error: Unmanaged<CFError>? guard let pkcs1Data = SecKeyCopyExternalRepresentation(privateKey, &error) as Data? else { throw error!.takeRetainedValue() as Error } // ECC 特定算法标识 let ecOID: Data if #available(iOS 10.0, *) { guard let attributes = SecKeyCopyAttributes(privateKey) as? [String: Any], let keySize = attributes[kSecAttrKeySizeInBits as String] as? Int else { throw KeyConversionError.invalidKeyAttributes } // 根据密钥长度选择正确的OID switch keySize { case 256: ecOID = Data([0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]) case 384: ecOID = Data([/* 对应OID */]) default: throw KeyConversionError.unsupportedKeySize } } else { throw KeyConversionError.unsupportedPlatform } // 构建PKCS8结构 let version = Data([0x02, 0x01, 0x00]) // 版本0 let pkcs8Data = version + ecOID + Data([0x04]) + derLength(for: pkcs1Data) + pkcs1Data let finalData = Data([0x30]) + derLength(for: pkcs8Data) + pkcs8Data // 转换为PEM let base64Content = finalData.base64EncodedString(options: .lineLength64Characters) return "-----BEGIN PRIVATE KEY-----\n\(base64Content)\n-----END PRIVATE KEY-----" }- 多算法支持框架:
enum KeyAlgorithm { case rsa case ecc(curve: ECCurve) enum ECCurve { case p256 case p384 case p521 } var algorithmOID: Data { switch self { case .rsa: return Data([0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00]) case .ecc(let curve): switch curve { case .p256: return Data([/* P-256 OID */]) case .p384: return Data([/* P-384 OID */]) case .p521: return Data([/* P-521 OID */]) } } } } struct KeyConverter { let algorithm: KeyAlgorithm func convertToPKCS8(privateKey: SecKey) throws -> String { // 通用转换逻辑,使用algorithm.algorithmOID } }14. 密钥转换的性能基准测试
了解不同实现的性能特征对于选择合适方案至关重要:
func runPerformanceBenchmark() { let keySizes = [1024, 2048, 4096] let iterations = 100 for size in keySizes { print("\n测试密钥大小: \(size) bits") // 生成测试密钥 let testKey = generateTestKey(size: size) // 测试方案一 var time = measureTime { for _ in 0..<iterations { _ = try? convertToPKCS8(privateKey: testKey) } } print("方案一平均时间: \(time/Double(iterations))秒") // 测试方案二 time = measureTime { for _ in 0..<iterations { _ = try? convertToPKCS8UsingSecurityFramework(privateKey: testKey) } } print("方案二平均时间: \(time/Double(iterations))秒") } } private func measureTime(_ block: () -> Void) -> Double { let start = CFAbsoluteTimeGetCurrent() block() return CFAbsoluteTimeGetCurrent() - start }典型结果可能如下:
| 密钥大小 | 方案一平均时间 | 方案二平均时间 |
|---|---|---|
| 1024位 | 0.002秒 | 0.005秒 |
| 2048位 | 0.004秒 | 0.008秒 |
| 4096位 | 0.015秒 | 0.025秒 |
15. 安全考虑与最佳实践
- 内存安全处理:
defer { // 确保敏感数据被清除 privateKeyData.withUnsafeMutableBytes { (buffer: UnsafeMutableRawBufferPointer) in memset(buffer.baseAddress!, 0, buffer.count) } }- 密钥使用权限控制:
let accessControl = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage, .userPresence], nil)! let keyAttributes: [String: Any] = [ kSecAttrAccessControl as String: accessControl, kSecAttrIsExtractable as String: true, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave ]- 审计日志记录:
func logKeyOperation(_ operation: String, success: Bool, metadata: [String: Any] = [:]) { let logEntry: [String: Any] = [ "timestamp": Date().timeIntervalSince1970, "operation": operation, "success": success, "device": UIDevice.current.identifierForVendor?.uuidString ?? "unknown", "metadata": metadata ] // 安全地记录到文件或发送到服务器 // 注意: 不应记录实际的密钥内容 }- 防篡改机制:
func verifyKeyIntegrity(key: SecKey) -> Bool { guard let publicKey = SecKeyCopyPublicKey(key) else { return false } let testData = Data(count: 32) var error: Unmanaged<CFError>? guard let signature = SecKeyCreateSignature( key, .rsaSignatureMessagePKCS1v15SHA256, testData as CFData, &error) else { return false } let verifyStatus = SecKeyVerifySignature( publicKey, .rsaSignatureMessagePKCS1v15SHA256, testData as CFData, signature, &error) return verifyStatus }16. 密钥转换在项目中的典型应用场景
- 与后端服务通信:
struct APIClient { private let cryptoService: CryptoService func sendEncryptedRequest(_ request: Request) throws -> Response { // 1. 获取当前密钥 let privateKey = try keychainStorage.retrieveKey(identifier: "currentKey") // 2. 转换为PKCS#8格式 let pemKey = try cryptoService.exportKey(privateKey: privateKey) // 3. 使用密钥签名请求 let signature = try cryptoService.sign(request.data, with: privateKey) // 4. 发送到服务器 let payload = Payload(data: request.data, key: pemKey, signature: signature) return try post(to: endpoint, payload: payload) } }- 密钥备份与恢复:
class KeyBackupManager { func backupKey(privateKey: SecKey, to url: URL) throws { // 转换为PKCS#8 PEM格式 let pemKey = try convertToPKCS8(privateKey: privateKey) // 加密备份文件 let encryptedData = try encryptData(Data(pemKey.utf8)) // 写入安全位置 try encryptedData.write(to: url, options: .completeFileProtection) } func restoreKey(from url: URL) throws -> SecKey { // 读取并解密 let encryptedData = try Data(contentsOf: url) let pemKey = try decryptData(encryptedData) // 从PEM导入 return try importPKCS8Key(pemKey) } }- 多平台密钥共享:
struct CrossPlatformKeySharer { func shareKey(privateKey: SecKey, with devices: [Device]) throws { // 转换为通用格式 let pemKey = try convertToPKCS8(privateKey: privateKey) // 为每个设备加密 let encryptedKeys = try devices.map { device in try encryptKey(pemKey, for: device) } // 分发密钥 try distributeEncryptedKeys(encryptedKeys) } private func encryptKey(_ key: String, for device: Device) throws -> Data { // 使用设备公钥加密 let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256 guard SecKeyIsAlgorithmSupported(device.publicKey, .