创建一个高效的文件管理类
基于NSFileManager和CryptoSwift封装一个简便的文件管理器,包含文件的操作及AES加解密
1.iOS的沙盒机制:
每个iOS应用都有自己的应用沙盒,应用沙盒就是文件系统目录。sandbox对应用程序执行各种操作的权限限制,是一种独立、安全、封闭的空间,非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等。其特点有:
- 每个应用程序的活动范围都限定在自己的沙盒里。
- 不能随意跨越自己的沙盒去访问别的应用程序沙盒中的内容(APP之间不能相互通,唯独可以通过URL Scheme可以通信)。
- 在访问别人沙盒内的数据时需要访问权限。
- 每此项目杀死后重新运行对应的沙盒路径是永不相同的。
沙盒的文件目录:
沙盒内部文件.jpg对应文件夹
- Document:一般需要持久的数据都放在此目录中,可以在当中添加子文件夹,iTunes备份和恢复的时候,会包括此目录。
- Library:设置程序的默认设置和其他状态信息
- Caches:一般存储的是缓存文件,例如图片视频等,此目录下的文件不会再应用程序退出时删除。在手机备份的时候,iTunes不会备份该目录。例如音频,视频等文件存放其中
-Preferences:保存应用程序的所有偏好设置iOS的Settings(设置),我们不应该直接在这里创建文件,而是需要通过NSUserDefault这个类来访问应用程序的偏好设置。iTunes会自动备份该文件目录下的内容。比如说:是否允许访问图片,是否允许访问地理位置......
- tmp:临时文件目录,在程序重新运行的时候,和开机的时候,会清空tmp文件夹。
2.CryptoSwift:
CryptoSwift是一个使用 Swift 编写的加密工具包,支持多种加密算法,如:MD5、SHA1、AES-128 等等。
管理类的基本框架设计
文件管理类框架.png基本方法
/* 获取沙盒路径方法 */
public func sandboxCachePath() -> String {
let array = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
return array.first ?? ""
}
/* 判断文件是否存在,如果有同名文件夹存在,依然会返回false */
public func isFileExist(at path: String) -> Bool {
var isDir: ObjCBool = false
let isExist = FileManager.default.fileExists(atPath: path, isDirectory: &isDir)
return isExist && !isDir.boolValue
}
/** 判断文件夹是否存在,如果有同名的文件存在,依然会返回false */
public func isDirectoryExist(at path: String) -> Bool {
var isDir: ObjCBool = false
let isExist = FileManager.default.fileExists(atPath: path, isDirectory: &isDir)
return isExist && isDir.boolValue
}
/** 创建一个文件夹目录,如果Path中包含不存在的目录,会自动创建这些目录。如果目录已存在则会忽略该操作。返回创建是否成功。 */
@discardableResult
public func createDirectory(_ dirPath: String) -> Bool {
guard isDirectoryExist(at: dirPath) == false else { return true }
do {
try FileManager.default.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil)
return true
} catch {
RLog.error("Create directory \(dirPath) throw an error")
}
return false
}
/** 删除文件夹或文件 */
@discardableResult
public func removeItem(at path: String) -> Bool {
do {
try FileManager.default.removeItem(atPath: path)
return true
} catch {
RLog.error("Remove item at \(path) throw an error")
}
return false
}
AES加/解密
AES加/解密模式
-
ECB 模式: 是最简单的块密码加密模式,加密前根据加密块大小(如 AES 为 128 位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。ECB 模式由于每块数据的加密是独立的因此加密和解密都可以并行计算。
-
CBC 模式:CBC 模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。
-
CTR 模式:在 CTR 模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密。但是在计算器不能维持很长的情况下,密钥只能使用一次。
-
CFB 模式: 与 ECB 和 CBC 模式只能够加密块数据不同,CFB 能够将块密文(Block Cipher)转换为流密文(Stream Cipher)。CFB 的加密工作分为两部分:先将前一段加密得到的密文再加密;接着将第 1 步加密得到的数据与当前段的明文异或。由于加密流程和解密流程中被块加密器加密的数据是前一段密文,因此即使明文数据的长度不是加密块大小的整数倍也是不需要填充的,这保证了数据长度在加密前后是相同的。
-
OFB 模式:OFB 是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。
密钥的长度
在进行 AES 加密时,CryptoSwift 会根据密钥的长度自动选择对应的加密算法(AES128, AES192, AES256)
- AES-128 = 16 bytes
- AES-192 = 24 bytes
- AES-256 = 32 bytes
这里以CBC的方式进行加密
/** 返回是否加密成功 */
@discardableResult
public func aesEncryptFile(at path: URL, key: Array<UInt8>, iv: Array<UInt8>) -> Bool {
do {
let originData = try Data(contentsOf: path)
let aes = try AES(key: key, blockMode: CBC(iv: iv))
let encryptBytes = try aes.encrypt(originData.bytes)
let encryptData = Data(bytes: encryptBytes)
try encryptData.write(to: path)
return true
} catch {
RLog.error("Encrypt file at path = \(path) throw an error")
}
return false
}
/** 返回是否解密成功 */
public func aesDecryptFile(at path: URL, key: Array<UInt8>, iv: Array<UInt8>) -> Data? {
do {
let originData = try Data(contentsOf: path)
let aes = try AES( key: key, blockMode: CBC(iv: iv))
let decryptBytes = try aes.decrypt(originData.bytes)
return Data(bytes: decryptBytes)
} catch {
RLog.error("Decrypt file at path = \(path) throw an error")
}
return nil
}