Files
xiaozhi-user-uniappx/uni_modules/laoqianjunzi-crypto/utssdk/app-ios/NativeCipherCore.swift
T

1131 lines
39 KiB
Swift

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<CFError>?
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[..<index])
}
if value != 0 {
throw cipherError("填充无效")
}
}
throw cipherError("填充无效")
case .zero:
var output = data
while output.last == 0 {
output.removeLast()
}
return output
}
}
private func cryptWithMode(operation: CCOperation, algorithm: CCAlgorithm, mode: CCMode, options: CCModeOptions, key: Data, iv: Data?, input: Data) throws -> 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)..<output.count)
return output
}
private func ivDataPointer<T>(_ 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..<output.count)
return output
}
private func hashData(_ input: Data, length: Int, body: (UnsafeMutableRawBufferPointer, UnsafeRawPointer?, Int) -> 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<CFError>?
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<CFError>?
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..<next)
var error: Unmanaged<CFError>?
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..<endIndex])
}
return "-----BEGIN \(header)-----\n\(lines.joined(separator: "\n"))\n-----END \(header)-----"
}
private func wrapRsaPublicKeyToSpki(_ pkcs1: Data) -> 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..<bitString.endIndex)
guard let first = bitStringData.first, first == 0x00 else {
throw cipherError("PEM无效")
}
return Data(bitStringData.dropFirst())
}
private func unwrapPkcs8PrivateKey(_ der: Data) throws -> 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..<octetString.endIndex)
}
private struct Asn1Element {
let contentStartIndex: Int
let endIndex: Int
}
private func readAsn1Element(from data: Data, at index: Int, expectedTag: UInt8) throws -> 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..<byteCount {
length = (length << 8) | Int(data[index + 1 + offset])
}
return (length, index + 1 + byteCount)
}
private func asn1Wrap(tag: UInt8, content: Data) -> 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..<integer.endIndex))
}
private func extractRsaModulus(fromPrivatePkcs1 data: Data) throws -> 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..<modulus.endIndex))
}
private func trimmedInteger(_ data: Data) -> 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<CFError>?, 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..<next]
guard let value = UInt8(pair, radix: 16) else {
throw cipherError("Hex无效")
}
output.append(value)
index = next
}
self = output
}
func hexString() -> String {
map { String(format: "%02x", $0) }.joined()
}
}