感觉iOS自带的CryptoKit不好用,有个第三方库CryptoSwift还不错,好巧不巧,清理过Xcode缓存后死活下载不下来,当然也可以自己编译个Framework,但是偏偏不想用第三方库了,于是研究了一下,自带的CommonCrypto也可以达到项目需求。
代码主要包含以下算法:
AES128/CBC/NoPadding
AES128/CTR/NoPadding
AES-CMAC
import Foundation
import CommonCrypto
class AESUtil {
private init(){}
///
///AES-CMAC
///
static func CMAC(key: Data, data: Data) -> Data? {
let blockSize = 16
var subKey1 = Data(count: blockSize)
var subKey2 = Data(count: blockSize)
// Step 1: Generate subkeys
guard generateSubKeys(key: key, subKey1: &subKey1, subKey2: &subKey2) else {
return nil
}
// Step 2: Calculate the number of blocks
let blockCount = (data.count + blockSize - 1) / blockSize
// Step 3: Process each block
var lastBlock = Data(count: blockSize)
for i in 0..<blockCount {
let blockRange = i * blockSize..<min((i + 1) * blockSize, data.count)
var block = data.subdata(in: blockRange)
if i == blockCount - 1 {
if block.count < blockSize {
block.append(0x80)
while block.count < blockSize {
block.append(0x00)
}
block = xor(data: block, with: subKey2)
} else {
block = xor(data: block, with: subKey1)
}
}
lastBlock = xor(data: lastBlock, with: block)
lastBlock = CBC(key: key, data: lastBlock, isEncrypt: true)!
}
return lastBlock
}
private static func generateSubKeys(key: Data, subKey1: inout Data, subKey2: inout Data) -> Bool {
let blockSize = 16
let zeroBlock = Data(count: blockSize)
guard let L = CBC(key: key, data: zeroBlock, isEncrypt: true) else {
return false
}
subKey1 = generateSubKey(block: L)
subKey2 = generateSubKey(block: subKey1)
return true
}
private static func generateSubKey(block: Data) -> Data {
let blockSize = 16
var subKey = Data(count: 16)
var overflow = false
for i in (0..<blockSize).reversed() {
let byte = block[i]
let shiftedByte = byte << 1
subKey[i] = shiftedByte | (overflow ? 1 : 0)
overflow = (byte & 0x80) != 0
}
if overflow {
subKey[blockSize - 1] ^= 0x87
}
return subKey
}
private static func xor(data: Data, with other: Data) -> Data {
var result = Data(count: data.count)
for i in 0..<data.count {
result[i] = data[i] ^ other[i]
}
return result
}
///
///AES128/CBC/NoPadding加解密
///
///@param isEncrypt true加密,false解密
///
static func CBC(key: Data, data: Data, isEncrypt: Bool) -> Data? {
return AES128NoPadding(key: key, iv: Data(count: 16), data: data, mode: "CBC", isEncrypt: isEncrypt)
}
///
///AES128/CTR/NoPadding加解密
///
///@param isEncrypt true加密,false解密
///
static func CTR(key: Data, data: Data, isEncrypt: Bool) -> Data? {
return AES128NoPadding(key: key, iv: Data(count: 16), data: data, mode: "CTR", isEncrypt: isEncrypt)
}
///
///AES128/NoPadding加解密
///
///@param mode 支持CBC、CTR
///@param isEncrypt true加密,false解密
///
static func AES128NoPadding(key: Data, iv: Data, data: Data, mode: String, isEncrypt: Bool) -> Data? {
let bufferLength = data.count + kCCKeySizeAES128
var buffer = Data(count: bufferLength)
var numBytesEncrypted: size_t = 0
let operation = isEncrypt ? kCCEncrypt : kCCDecrypt
let cryptStatus: CCCryptorStatus = buffer.withUnsafeMutableBytes { (bufferPtr: UnsafeMutableRawBufferPointer) in
key.withUnsafeBytes { (keyPtr: UnsafeRawBufferPointer) in
iv.withUnsafeBytes { (ivPtr: UnsafeRawBufferPointer) in
data.withUnsafeBytes { (dataPtr: UnsafeRawBufferPointer) in
//调用加密函数
var modeSource = 0
if mode == "CBC" {
modeSource = kCCModeCBC
} else if mode == "CTR" {
modeSource = kCCModeCTR
}
let cryptorRef = UnsafeMutablePointer<CCCryptorRef?>.allocate(capacity: 1)
var status = CCCryptorCreateWithMode(CCOperation(operation), CCMode(modeSource), CCAlgorithm(kCCAlgorithmAES), CCPadding(ccNoPadding), ivPtr.baseAddress, keyPtr.baseAddress, kCCKeySizeAES128, nil, 0, 0, CCModeOptions(0), cryptorRef)
if status == kCCSuccess {
status = CCCryptorUpdate(cryptorRef.pointee, dataPtr.baseAddress, data.count, bufferPtr.baseAddress, bufferLength, &numBytesEncrypted)
} else {
print("CCCryptorCreateWithMode fail: \(encryptError(status))")
}
return status
}
}
}
}
if cryptStatus == kCCSuccess {
buffer.removeSubrange(numBytesEncrypted..<bufferLength)
return buffer
}
print("AES/\(mode)/NoPadding加解密失败: \(encryptError(cryptStatus))")
return nil
}
private static func encryptError(_ status: CCCryptorStatus)-> String {
if status == kCCParamError {
return "kCCParamError"
} else if status == kCCBufferTooSmall {
return "kCCBufferTooSmall"
} else if status == kCCMemoryFailure {
return "kCCMemoryFailure"
} else if status == kCCAlignmentError {
return "kCCAlignmentError"
} else if status == kCCDecodeError {
return "kCCDecodeError"
} else if status == kCCUnimplemented {
return "kCCUnimplemented"
} else if status == kCCOverflow {
return "kCCOverflow"
} else if status == kCCRNGFailure {
return "kCCRNGFailure"
} else if status == kCCUnspecifiedError {
return "kCCUnspecifiedError"
} else if status == kCCCallSequenceError {
return "kCCCallSequenceError"
} else if status == kCCKeySizeError {
return "kCCKeySizeError"
} else if status == kCCInvalidKey {
return "kCCInvalidKey"
}
return "\(status)"
}
}