import Foundation import CommonCrypto import Security public struct NativeRsaBundle { public let publicKey: String public let privateKey: String public init(publicKey: String, privateKey: String) { self.publicKey = publicKey self.privateKey = privateKey } } public enum NativeCipherCore { public static func digestHex(kind: String, plainText: String) throws -> String { let input = Data(plainText.utf8) let digest: Data switch normalize(kind) { case "MD5": digest = hashData(input, length: Int(CC_MD5_DIGEST_LENGTH)) { buffer, bytes, count in _ = CC_MD5(bytes, CC_LONG(count), buffer.bindMemory(to: UInt8.self).baseAddress) } case "SHA1": digest = hashData(input, length: Int(CC_SHA1_DIGEST_LENGTH)) { buffer, bytes, count in _ = CC_SHA1(bytes, CC_LONG(count), buffer.bindMemory(to: UInt8.self).baseAddress) } case "SHA256": digest = hashData(input, length: Int(CC_SHA256_DIGEST_LENGTH)) { buffer, bytes, count in _ = CC_SHA256(bytes, CC_LONG(count), buffer.bindMemory(to: UInt8.self).baseAddress) } case "SHA512": digest = hashData(input, length: Int(CC_SHA512_DIGEST_LENGTH)) { buffer, bytes, count in _ = CC_SHA512(bytes, CC_LONG(count), buffer.bindMemory(to: UInt8.self).baseAddress) } default: throw cipherError("不支持的摘要算法") } return digest.hexString() } public static func hmacHex(kind: String, secretText: String, plainText: String) throws -> String { let algorithm: CCHmacAlgorithm let digestLength: Int switch normalize(kind) { case "SHA1": algorithm = CCHmacAlgorithm(kCCHmacAlgSHA1) digestLength = Int(CC_SHA1_DIGEST_LENGTH) case "SHA256": algorithm = CCHmacAlgorithm(kCCHmacAlgSHA256) digestLength = Int(CC_SHA256_DIGEST_LENGTH) case "SHA512": algorithm = CCHmacAlgorithm(kCCHmacAlgSHA512) digestLength = Int(CC_SHA512_DIGEST_LENGTH) default: throw cipherError("不支持的HMAC算法") } let keyData = Data(secretText.utf8) let inputData = Data(plainText.utf8) var output = Data(count: digestLength) output.withUnsafeMutableBytes { outputBuffer in keyData.withUnsafeBytes { keyBuffer in inputData.withUnsafeBytes { inputBuffer in CCHmac( algorithm, keyBuffer.baseAddress, keyData.count, inputBuffer.baseAddress, inputData.count, outputBuffer.baseAddress ) } } } return output.hexString() } public static func encodeBase64(plainText: String) -> String { Data(plainText.utf8).base64EncodedString() } public static func decodeBase64(encodedText: String) throws -> String { guard let data = Data(base64Encoded: encodedText) else { throw cipherError("Base64无效") } guard let text = String(data: data, encoding: .utf8) else { throw cipherError("文本解码失败") } return text } public static func encodeCodec(codec: String, plainText: String) throws -> String { let input = Data(plainText.utf8) switch normalize(codec) { case "UTF8": return plainText case "HEX": return input.hexString() case "BASE64": return input.base64EncodedString() case "BASE64URL": return input.base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=+$", with: "", options: .regularExpression) case "LATIN1": return latin1String(from: input) case "UTF16": return utf16CodecString(from: input) default: throw cipherError("不支持的编码类型") } } public static func decodeCodec(codec: String, encodedText: String) throws -> String { switch normalize(codec) { case "UTF8": return encodedText case "HEX": return try utf8String(from: Data(hexString: encodedText)) case "BASE64": return try decodeBase64(encodedText: encodedText) case "BASE64URL": var normalized = encodedText.replacingOccurrences(of: "-", with: "+") .replacingOccurrences(of: "_", with: "/") while normalized.count % 4 != 0 { normalized.append("=") } return try decodeBase64(encodedText: normalized) case "LATIN1": return try utf8String(from: dataFromLatin1String(encodedText)) case "UTF16": return try utf8String(from: dataFromUtf16CodecString(encodedText)) default: throw cipherError("不支持的编码类型") } } public static func aesEncryptText(secretText: String, plainText: String, mode: String, padding: String, ivText: String?, keyLength: Int?) throws -> String { let cipherBytes = try aesEncryptBytes( secretBytes: Array(Data(secretText.utf8)), plainBytes: Array(Data(plainText.utf8)), mode: mode, padding: padding, ivBytes: ivText.map { Array(Data($0.utf8)) }, keyLength: keyLength ) return Data(cipherBytes).base64EncodedString() } public static func aesDecryptText(secretText: String, cipherText: String, mode: String, padding: String, ivText: String?, keyLength: Int?) throws -> String { guard let cipherData = Data(base64Encoded: cipherText) else { throw cipherError("Base64无效") } let plainBytes = try aesDecryptBytes( secretBytes: Array(Data(secretText.utf8)), cipherBytes: Array(cipherData), mode: mode, padding: padding, ivBytes: ivText.map { Array(Data($0.utf8)) }, keyLength: keyLength ) guard let text = String(data: Data(plainBytes), encoding: .utf8) else { throw cipherError("文本解码失败") } return text } public static func aesEncryptBytes(secretBytes: [UInt8], plainBytes: [UInt8], mode: String, padding: String, ivBytes: [UInt8]?, keyLength: Int?) throws -> [UInt8] { let config = try makeAesConfig(mode: mode, padding: padding, keyLength: keyLength) let key = try normalizeKey(secretBytes, to: config.keyLength) let iv = try normalizeIV(ivBytes, blockSize: kCCBlockSizeAES128, mode: config.mode) let plainData = Data(plainBytes) let padded = try applyPaddingIfNeeded(plainData, blockSize: kCCBlockSizeAES128, padding: config.padding, mode: config.mode) let encrypted = try cryptWithMode( operation: CCOperation(kCCEncrypt), algorithm: CCAlgorithm(kCCAlgorithmAES), mode: config.mode.ccMode, options: config.mode.ccOptions, key: key, iv: iv, input: padded ) return Array(encrypted) } public static func aesDecryptBytes(secretBytes: [UInt8], cipherBytes: [UInt8], mode: String, padding: String, ivBytes: [UInt8]?, keyLength: Int?) throws -> [UInt8] { let config = try makeAesConfig(mode: mode, padding: padding, keyLength: keyLength) let key = try normalizeKey(secretBytes, to: config.keyLength) let iv = try normalizeIV(ivBytes, blockSize: kCCBlockSizeAES128, mode: config.mode) let decrypted = try cryptWithMode( operation: CCOperation(kCCDecrypt), algorithm: CCAlgorithm(kCCAlgorithmAES), mode: config.mode.ccMode, options: config.mode.ccOptions, key: key, iv: iv, input: Data(cipherBytes) ) let plainData = try removePaddingIfNeeded(decrypted, blockSize: kCCBlockSizeAES128, padding: config.padding, mode: config.mode) return Array(plainData) } public static func tripleDesEncryptText(secretText: String, plainText: String, mode: String, ivText: String?) throws -> String { let cipherData = try tripleDesTransform( operation: CCOperation(kCCEncrypt), secretBytes: Array(Data(secretText.utf8)), input: try applyPadding(Data(plainText.utf8), blockSize: kCCBlockSize3DES, padding: .pkcs7), modeName: mode, ivBytes: ivText.map { Array(Data($0.utf8)) } ) return cipherData.base64EncodedString() } public static func tripleDesDecryptText(secretText: String, cipherText: String, mode: String, ivText: String?) throws -> String { guard let cipherData = Data(base64Encoded: cipherText) else { throw cipherError("Base64无效") } let plainData = try tripleDesTransform( operation: CCOperation(kCCDecrypt), secretBytes: Array(Data(secretText.utf8)), input: cipherData, modeName: mode, ivBytes: ivText.map { Array(Data($0.utf8)) } ) let unpadded = try removePadding(plainData, blockSize: kCCBlockSize3DES, padding: .pkcs7) guard let text = String(data: unpadded, encoding: .utf8) else { throw cipherError("文本解码失败") } return text } public static func desEncryptText(secretText: String, plainText: String, mode: String, padding: String, ivText: String?) throws -> String { let config = try makeDesConfig(mode: mode, padding: padding) let key = try normalizeKey(Array(Data(secretText.utf8)), to: kCCKeySizeDES) let iv = try normalizeIV(ivText.map { Array(Data($0.utf8)) }, blockSize: kCCBlockSizeDES, mode: config.mode) let padded = try applyPaddingIfNeeded(Data(plainText.utf8), blockSize: kCCBlockSizeDES, padding: config.padding, mode: config.mode) let encrypted = try cryptWithMode( operation: CCOperation(kCCEncrypt), algorithm: CCAlgorithm(kCCAlgorithmDES), mode: config.mode.ccMode, options: config.mode.ccOptions, key: key, iv: iv, input: padded ) return encrypted.base64EncodedString() } public static func desDecryptText(secretText: String, cipherText: String, mode: String, padding: String, ivText: String?) throws -> String { guard let cipherData = Data(base64Encoded: cipherText) else { throw cipherError("Base64无效") } let config = try makeDesConfig(mode: mode, padding: padding) let key = try normalizeKey(Array(Data(secretText.utf8)), to: kCCKeySizeDES) let iv = try normalizeIV(ivText.map { Array(Data($0.utf8)) }, blockSize: kCCBlockSizeDES, mode: config.mode) let decrypted = try cryptWithMode( operation: CCOperation(kCCDecrypt), algorithm: CCAlgorithm(kCCAlgorithmDES), mode: config.mode.ccMode, options: config.mode.ccOptions, key: key, iv: iv, input: cipherData ) let plainData = try removePaddingIfNeeded(decrypted, blockSize: kCCBlockSizeDES, padding: config.padding, mode: config.mode) return try utf8String(from: plainData) } public static func rc4EncryptToHex(secretText: String, plainText: String) throws -> String { let cipher = try rc4Transform(secret: Data(secretText.utf8), input: Data(plainText.utf8)) return cipher.hexString() } public static func rc4DecryptFromHex(secretText: String, cipherHexText: String) throws -> String { let cipher = try Data(hexString: cipherHexText) let plain = try rc4Transform(secret: Data(secretText.utf8), input: cipher) guard let text = String(data: plain, encoding: .utf8) else { throw cipherError("文本解码失败") } return text } public static func createRsaBundle(keySize: Int) throws -> NativeRsaBundle { let normalizedKeySize = try normalizeRsaKeySize(keySize) let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits as String: normalizedKeySize, kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: false ], kSecPublicKeyAttrs as String: [ kSecAttrIsPermanent as String: false ] ] var error: Unmanaged? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { throw securityError(error, fallback: "RSA生成失败") } guard let publicKey = SecKeyCopyPublicKey(privateKey) else { throw cipherError("RSA公钥生成失败") } guard let privateData = SecKeyCopyExternalRepresentation(privateKey, &error) as Data? else { throw securityError(error, fallback: "导出私钥失败") } guard let publicPkcs1 = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else { throw securityError(error, fallback: "导出公钥失败") } let publicPem = pemString(header: "PUBLIC KEY", body: wrapRsaPublicKeyToSpki(publicPkcs1)) let privatePem = pemString(header: "RSA PRIVATE KEY", body: privateData) return NativeRsaBundle(publicKey: publicPem, privateKey: privatePem) } public static func rsaEncrypt(publicKey: String, plainText: String, outputKind: String?) throws -> String { let key = try makeRsaPublicKey(from: publicKey) let plainData = Data(plainText.utf8) let encrypted = try rsaChunkedTransform(key: key, data: plainData, encrypting: true) return try encodeCipherOutput(encrypted, kind: outputKind) } public static func rsaDecrypt(privateKey: String, cipherText: String, outputKind: String?) throws -> String { let key = try makeRsaPrivateKey(from: privateKey) let cipherData = try decodeCipherInput(cipherText, kind: outputKind) let decrypted = try rsaChunkedTransform(key: key, data: cipherData, encrypting: false) guard let text = String(data: decrypted, encoding: .utf8) else { throw cipherError("文本解码失败") } return text } } private enum NativeAesMode { case ecb case cbc case cfb case ctr case ctrGladman case ofb var ccMode: CCMode { switch self { case .ecb: return CCMode(kCCModeECB) case .cbc: return CCMode(kCCModeCBC) case .cfb: return CCMode(kCCModeCFB) case .ctr, .ctrGladman: return CCMode(kCCModeCTR) case .ofb: return CCMode(kCCModeOFB) } } var ccOptions: CCModeOptions { switch self { case .ctr: return CCModeOptions(kCCModeOptionCTR_BE) case .ctrGladman: // CommonCrypto 未单独提供 Gladman 计数器模式,这里映射为标准 CTR 兼容实现。 return CCModeOptions(kCCModeOptionCTR_BE) default: return CCModeOptions(0) } } var needsBlockAlignmentWithoutPadding: Bool { switch self { case .ecb, .cbc: return true default: return false } } var usesIV: Bool { switch self { case .ecb: return false default: return true } } } private enum NativePadding { case pkcs7 case ansiX923 case iso10126 case iso97971 case none case zero } private struct AesConfig { let mode: NativeAesMode let padding: NativePadding let keyLength: Int } private struct DesConfig { let mode: NativeAesMode let padding: NativePadding } private func makeAesConfig(mode: String, padding: String, keyLength: Int?) throws -> AesConfig { let aesMode: NativeAesMode switch normalize(mode) { case "ECB": aesMode = .ecb case "CBC": aesMode = .cbc case "CFB": aesMode = .cfb case "CTR": aesMode = .ctr case "CTRGLADMAN": aesMode = .ctrGladman case "OFB": aesMode = .ofb default: throw cipherError("不支持的AES模式") } let aesPadding: NativePadding switch normalize(padding) { case "PKCS7": aesPadding = .pkcs7 case "ANSI_X923": aesPadding = .ansiX923 case "ISO_10126": aesPadding = .iso10126 case "ISO_97971": aesPadding = .iso97971 case "NONE": aesPadding = .none case "ZERO": aesPadding = .zero default: throw cipherError("不支持的AES填充") } return AesConfig(mode: aesMode, padding: aesPadding, keyLength: try normalizeAesKeyLength(keyLength)) } private func makeDesConfig(mode: String, padding: String) throws -> DesConfig { let desMode: NativeAesMode switch normalize(mode) { case "ECB": desMode = .ecb case "CBC": desMode = .cbc case "CFB": desMode = .cfb case "CTR", "CTRGLADMAN": desMode = .ctr case "OFB": desMode = .ofb default: throw cipherError("不支持的DES模式") } let desPadding: NativePadding switch normalize(padding) { case "PKCS7": desPadding = .pkcs7 case "ANSI_X923": desPadding = .ansiX923 case "ISO_10126": desPadding = .iso10126 case "ISO_97971": desPadding = .iso97971 case "NONE": desPadding = .none case "ZERO": desPadding = .zero default: throw cipherError("不支持的DES填充") } return DesConfig(mode: desMode, padding: desPadding) } private func normalize(_ value: String) -> String { value.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() } private func normalizeAesKeyLength(_ keyLength: Int?) throws -> Int { guard let keyLength else { return kCCKeySizeAES256 } switch keyLength { case 16, 24, 32: return keyLength case 128, 192, 256: return keyLength / 8 default: throw cipherError("AES密钥长度无效") } } private func normalizeRsaKeySize(_ keySize: Int) throws -> Int { if keySize == 1024 || keySize == 2048 || keySize == 3072 || keySize == 4096 { return keySize } throw cipherError("RSA密钥长度无效") } private func normalizeKey(_ secretBytes: [UInt8], to length: Int) throws -> Data { if secretBytes.isEmpty { throw cipherError("密钥不能为空") } var key = Data(secretBytes) if key.count > length { key = key.prefix(length) } else if key.count < length { key.append(Data(repeating: 0, count: length - key.count)) } return key } private func normalizeIV(_ ivBytes: [UInt8]?, blockSize: Int, mode: NativeAesMode) throws -> Data? { guard mode.usesIV else { return nil } var iv = Data(ivBytes ?? []) if iv.count > blockSize { iv = iv.prefix(blockSize) } else if iv.count < blockSize { iv.append(Data(repeating: 0, count: blockSize - iv.count)) } return iv } private func applyPaddingIfNeeded(_ data: Data, blockSize: Int, padding: NativePadding, mode: NativeAesMode) throws -> Data { if padding == .none && !mode.needsBlockAlignmentWithoutPadding { return data } return try applyPadding(data, blockSize: blockSize, padding: padding) } private func removePaddingIfNeeded(_ data: Data, blockSize: Int, padding: NativePadding, mode: NativeAesMode) throws -> Data { if padding == .none && !mode.needsBlockAlignmentWithoutPadding { return data } return try removePadding(data, blockSize: blockSize, padding: padding) } private func applyPadding(_ data: Data, blockSize: Int, padding: NativePadding) throws -> Data { switch padding { case .none: if data.count % blockSize != 0 { throw cipherError("数据长度无效") } return data case .pkcs7: let remain = data.count % blockSize let actualPad = remain == 0 ? blockSize : blockSize - remain return data + Data(repeating: UInt8(actualPad), count: actualPad) case .ansiX923: let padCount = blockSize - (data.count % blockSize) let actualPad = padCount == 0 ? blockSize : padCount var result = data if actualPad > 1 { result.append(Data(repeating: 0, count: actualPad - 1)) } result.append(UInt8(actualPad)) return result case .iso10126: let padCount = blockSize - (data.count % blockSize) let actualPad = padCount == 0 ? blockSize : padCount var result = data if actualPad > 1 { var random = Data(count: actualPad - 1) let status = random.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, actualPad - 1, $0.baseAddress!) } if status != errSecSuccess { throw cipherError("随机填充失败") } result.append(random) } result.append(UInt8(actualPad)) return result case .iso97971: var result = data result.append(0x80) let remain = result.count % blockSize if remain != 0 { result.append(Data(repeating: 0, count: blockSize - remain)) } return result case .zero: let remain = data.count % blockSize if remain == 0 { return data } return data + Data(repeating: 0, count: blockSize - remain) } } private func removePadding(_ data: Data, blockSize: Int, padding: NativePadding) throws -> Data { switch padding { case .none: if data.count % blockSize != 0 { throw cipherError("数据长度无效") } return data case .pkcs7: guard let last = data.last else { return data } let pad = Int(last) guard pad > 0, pad <= blockSize, pad <= data.count else { throw cipherError("填充无效") } let tail = data.suffix(pad) if tail.allSatisfy({ $0 == last }) { return Data(data.dropLast(pad)) } throw cipherError("填充无效") case .ansiX923: guard let last = data.last else { return data } let pad = Int(last) guard pad > 0, pad <= blockSize, pad <= data.count else { throw cipherError("填充无效") } if pad > 1 && !data.dropLast().suffix(pad - 1).allSatisfy({ $0 == 0 }) { throw cipherError("填充无效") } return Data(data.dropLast(pad)) case .iso10126: guard let last = data.last else { return data } let pad = Int(last) guard pad > 0, pad <= blockSize, pad <= data.count else { throw cipherError("填充无效") } return Data(data.dropLast(pad)) case .iso97971: var index = data.endIndex while index > data.startIndex { index = data.index(before: index) let value = data[index] if value == 0x80 { return Data(data[.. Data { var cryptor: CCCryptorRef? let status = key.withUnsafeBytes { keyBuffer in ivDataPointer(iv) { ivPointer in CCCryptorCreateWithMode( operation, mode, algorithm, CCPadding(ccNoPadding), ivPointer, keyBuffer.baseAddress, key.count, nil, 0, 0, options, &cryptor ) } } guard status == kCCSuccess, let cryptor else { throw cipherError("加解密初始化失败") } defer { CCCryptorRelease(cryptor) } var output = Data(count: input.count + kCCBlockSizeAES128) var moved = 0 var finalMoved = 0 let updateStatus = output.withUnsafeMutableBytes { outputBuffer in input.withUnsafeBytes { inputBuffer in CCCryptorUpdate( cryptor, inputBuffer.baseAddress, input.count, outputBuffer.baseAddress, output.count, &moved ) } } guard updateStatus == kCCSuccess else { throw cipherError("加解密失败") } let finalStatus = output.withUnsafeMutableBytes { outputBuffer in CCCryptorFinal( cryptor, outputBuffer.baseAddress?.advanced(by: moved), output.count - moved, &finalMoved ) } guard finalStatus == kCCSuccess else { throw cipherError("加解密失败") } output.removeSubrange((moved + finalMoved)..(_ iv: Data?, _ body: (UnsafeRawPointer?) -> T) -> T { guard let iv else { return body(nil) } return iv.withUnsafeBytes { body($0.baseAddress) } } private func tripleDesTransform(operation: CCOperation, secretBytes: [UInt8], input: Data, modeName: String, ivBytes: [UInt8]?) throws -> Data { let mode: NativeAesMode switch normalize(modeName) { case "ECB": mode = .ecb case "CBC": mode = .cbc default: throw cipherError("不支持的3DES模式") } let key = try normalizeKey(secretBytes, to: kCCKeySize3DES) let iv = try normalizeIV(ivBytes, blockSize: kCCBlockSize3DES, mode: mode) return try cryptWithMode( operation: operation, algorithm: CCAlgorithm(kCCAlgorithm3DES), mode: mode.ccMode, options: CCModeOptions(0), key: key, iv: iv, input: input ) } private func rc4Transform(secret: Data, input: Data) throws -> Data { if secret.isEmpty { throw cipherError("密钥不能为空") } var output = Data(count: input.count + kCCBlockSizeRC2) var moved = 0 let status = output.withUnsafeMutableBytes { outputBuffer in secret.withUnsafeBytes { keyBuffer in input.withUnsafeBytes { inputBuffer in CCCrypt( CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmRC4), CCOptions(0), keyBuffer.baseAddress, secret.count, nil, inputBuffer.baseAddress, input.count, outputBuffer.baseAddress, output.count, &moved ) } } } guard status == kCCSuccess else { throw cipherError("RC4处理失败") } output.removeSubrange(moved.. Void) -> Data { var output = Data(count: length) output.withUnsafeMutableBytes { outputBuffer in input.withUnsafeBytes { inputBuffer in body(outputBuffer, inputBuffer.baseAddress, input.count) } } return output } private func utf8String(from data: Data) throws -> String { guard let text = String(data: data, encoding: .utf8) else { throw cipherError("文本解码失败") } return text } private func latin1String(from data: Data) -> String { var output = "" output.reserveCapacity(data.count) for byte in data { if let scalar = UnicodeScalar(Int(byte)) { output.append(Character(scalar)) } } return output } private func dataFromLatin1String(_ text: String) -> Data { var bytes = [UInt8]() bytes.reserveCapacity(text.count) for value in text.unicodeScalars { bytes.append(UInt8(value.value & 0xFF)) } return Data(bytes) } private func utf16CodecString(from data: Data) -> String { if data.isEmpty { return "" } var units = [UInt16]() units.reserveCapacity((data.count + 1) / 2) var index = data.startIndex while index < data.endIndex { let high = UInt16(data[index]) << 8 let nextIndex = data.index(after: index) let low: UInt16 = nextIndex < data.endIndex ? UInt16(data[nextIndex]) : 0 units.append(high | low) index = data.index(index, offsetBy: 2, limitedBy: data.endIndex) ?? data.endIndex } return String(utf16CodeUnits: units, count: units.count) } private func dataFromUtf16CodecString(_ text: String) -> Data { let units = Array(text.utf16) var bytes = [UInt8]() bytes.reserveCapacity(units.count * 2) for unit in units { bytes.append(UInt8((unit >> 8) & 0xFF)) bytes.append(UInt8(unit & 0xFF)) } return Data(bytes) } private func makeRsaPublicKey(from pem: String) throws -> SecKey { let body = try decodePemBody(from: pem) let raw = pem.contains("BEGIN PUBLIC KEY") ? try unwrapSubjectPublicKeyInfo(body) : body let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits as String: rsaBitLength(fromPkcs1: raw) ] var error: Unmanaged? guard let key = SecKeyCreateWithData(raw as CFData, attributes as CFDictionary, &error) else { throw securityError(error, fallback: "公钥无效") } return key } private func makeRsaPrivateKey(from pem: String) throws -> SecKey { let body = try decodePemBody(from: pem) let raw = pem.contains("BEGIN PRIVATE KEY") && !pem.contains("BEGIN RSA PRIVATE KEY") ? try unwrapPkcs8PrivateKey(body) : body let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits as String: rsaBitLength(fromPrivateKey: raw) ] var error: Unmanaged? guard let key = SecKeyCreateWithData(raw as CFData, attributes as CFDictionary, &error) else { throw securityError(error, fallback: "私钥无效") } return key } private func rsaChunkedTransform(key: SecKey, data: Data, encrypting: Bool) throws -> Data { let algorithm: SecKeyAlgorithm = .rsaEncryptionPKCS1 guard SecKeyIsAlgorithmSupported(key, encrypting ? .encrypt : .decrypt, algorithm) else { throw cipherError(encrypting ? "RSA加密不可用" : "RSA解密不可用") } let blockSize = SecKeyGetBlockSize(key) let chunkSize = encrypting ? blockSize - 11 : blockSize if chunkSize <= 0 { throw cipherError("RSA块大小无效") } var result = Data() var offset = 0 while offset < data.count { let next = min(offset + chunkSize, data.count) let chunk = data.subdata(in: offset..? let transformed: Data? if encrypting { transformed = SecKeyCreateEncryptedData(key, algorithm, chunk as CFData, &error) as Data? } else { transformed = SecKeyCreateDecryptedData(key, algorithm, chunk as CFData, &error) as Data? } guard let transformed else { throw securityError(error, fallback: encrypting ? "RSA加密失败" : "RSA解密失败") } result.append(transformed) offset = next } return result } private func encodeCipherOutput(_ data: Data, kind: String?) throws -> String { switch normalize(kind ?? "base64") { case "BASE64": return data.base64EncodedString() case "HEX": return data.hexString() default: throw cipherError("RSA输出格式无效") } } private func decodeCipherInput(_ text: String, kind: String?) throws -> Data { switch normalize(kind ?? "base64") { case "BASE64": guard let data = Data(base64Encoded: text) else { throw cipherError("Base64无效") } return data case "HEX": return try Data(hexString: text) default: throw cipherError("RSA输入格式无效") } } private func decodePemBody(from pem: String) throws -> Data { let lines = pem .components(separatedBy: .newlines) .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty && !$0.hasPrefix("-----BEGIN") && !$0.hasPrefix("-----END") } let joined = lines.joined() guard let data = Data(base64Encoded: joined) else { throw cipherError("PEM无效") } return data } private func pemString(header: String, body: Data) -> String { let content = body.base64EncodedString() let lines = stride(from: 0, to: content.count, by: 64).map { start -> String in let startIndex = content.index(content.startIndex, offsetBy: start) let endIndex = content.index(startIndex, offsetBy: min(64, content.count - start)) return String(content[startIndex.. Data { let algorithmIdentifier = Data([0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00]) let bitString = asn1Wrap(tag: 0x03, content: Data([0x00]) + pkcs1) return asn1Wrap(tag: 0x30, content: algorithmIdentifier + bitString) } private func unwrapSubjectPublicKeyInfo(_ der: Data) throws -> Data { let sequence = try readAsn1Element(from: der, at: 0, expectedTag: 0x30) var cursor = sequence.contentStartIndex let algorithm = try readAsn1Element(from: der, at: cursor, expectedTag: 0x30) cursor = algorithm.endIndex let bitString = try readAsn1Element(from: der, at: cursor, expectedTag: 0x03) let bitStringData = der.subdata(in: bitString.contentStartIndex.. Data { let sequence = try readAsn1Element(from: der, at: 0, expectedTag: 0x30) var cursor = sequence.contentStartIndex let version = try readAsn1Element(from: der, at: cursor, expectedTag: 0x02) cursor = version.endIndex let algorithm = try readAsn1Element(from: der, at: cursor, expectedTag: 0x30) cursor = algorithm.endIndex let octetString = try readAsn1Element(from: der, at: cursor, expectedTag: 0x04) return der.subdata(in: octetString.contentStartIndex.. Asn1Element { guard index < data.count, data[index] == expectedTag else { throw cipherError("PEM无效") } let lengthInfo = try readAsn1Length(from: data, at: index + 1) let contentStart = lengthInfo.nextIndex let end = contentStart + lengthInfo.length guard end <= data.count else { throw cipherError("PEM无效") } return Asn1Element(contentStartIndex: contentStart, endIndex: end) } private func readAsn1Length(from data: Data, at index: Int) throws -> (length: Int, nextIndex: Int) { guard index < data.count else { throw cipherError("PEM无效") } let first = data[index] if first & 0x80 == 0 { return (Int(first), index + 1) } let byteCount = Int(first & 0x7F) guard byteCount > 0, index + byteCount < data.count else { throw cipherError("PEM无效") } var length = 0 for offset in 0.. Data { var output = Data([tag]) output.append(asn1LengthBytes(content.count)) output.append(content) return output } private func asn1LengthBytes(_ length: Int) -> Data { if length < 0x80 { return Data([UInt8(length)]) } var value = length var bytes: [UInt8] = [] while value > 0 { bytes.insert(UInt8(value & 0xFF), at: 0) value >>= 8 } return Data([0x80 | UInt8(bytes.count)] + bytes) } private func rsaBitLength(fromPkcs1 data: Data) -> Int { if let modulus = try? extractRsaModulus(fromPublicPkcs1: data) { return modulusBitLength(modulus) } return max(1024, data.count * 8) } private func rsaBitLength(fromPrivateKey data: Data) -> Int { if let modulus = try? extractRsaModulus(fromPrivatePkcs1: data) { return modulusBitLength(modulus) } return max(1024, data.count * 8) } private func extractRsaModulus(fromPublicPkcs1 data: Data) throws -> Data { let sequence = try readAsn1Element(from: data, at: 0, expectedTag: 0x30) let integer = try readAsn1Element(from: data, at: sequence.contentStartIndex, expectedTag: 0x02) return trimmedInteger(data.subdata(in: integer.contentStartIndex.. Data { let sequence = try readAsn1Element(from: data, at: 0, expectedTag: 0x30) var cursor = sequence.contentStartIndex let version = try readAsn1Element(from: data, at: cursor, expectedTag: 0x02) cursor = version.endIndex let modulus = try readAsn1Element(from: data, at: cursor, expectedTag: 0x02) return trimmedInteger(data.subdata(in: modulus.contentStartIndex.. Data { var result = data while result.count > 1 && result.first == 0x00 { result.removeFirst() } return result } private func modulusBitLength(_ modulus: Data) -> Int { guard let first = modulus.first else { return 0 } var bits = modulus.count * 8 var mask: UInt8 = 0x80 while mask > 0, first & mask == 0 { bits -= 1 mask >>= 1 } return bits } private func cipherError(_ message: String) -> NSError { NSError(domain: "NativeCipherCore", code: -1, userInfo: [NSLocalizedDescriptionKey: message]) } private func securityError(_ error: Unmanaged?, fallback: String) -> NSError { if let error { let message = CFErrorCopyDescription(error.takeRetainedValue()) as String return cipherError(message.isEmpty ? fallback : message) } return cipherError(fallback) } private extension Data { init(hexString: String) throws { let cleaned = hexString.trimmingCharacters(in: .whitespacesAndNewlines) guard cleaned.count % 2 == 0 else { throw cipherError("Hex无效") } var output = Data(capacity: cleaned.count / 2) var index = cleaned.startIndex while index < cleaned.endIndex { let next = cleaned.index(index, offsetBy: 2) let pair = cleaned[index.. String { map { String(format: "%02x", $0) }.joined() } }