CommonCrypto 和 Security 框架的 Swift 封装
支持的算法:MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512。
流式哈希计算器。
let sha = SHA256()
sha.update(data: Data("12".utf8))
sha.update(data: [1, 2])
let degest = sha.finalize()
一次性计算。
let digest = SHA256.hash(data: Data("123".utf8))
所有哈希函数都返回符合 Digest
协议的类型。 你可以将摘要转换为常见的类型,例如 Data
和 [UInt8]
。
let data = Data(digest)
let bytes: [UInt8] = Array(digest)
基于哈希的消息认证码
流式哈希计算器。
let key = Data("secret_key".utf8)
let hmac = HMAC(algorithm: .md5, key: key)
hmac.update(data: Data("ab".utf8))
hmac.update(data: Data("cd".utf8))
let hash = hmac.finalize()
一次性计算。
let key = Data("secret_key".utf8)
let data = Data("abcd".utf8)
let hash = HMAC.hash(algorithm: .sha224, data: data, key: key)
$ mkdir gyb
$ cd gyb
$ wget https://github.com/apple/swift/raw/master/utils/gyb
$ wget https://github.com/apple/swift/raw/master/utils/gyb.py
$ chmod +x gyb
要计算大文件的哈希,请使用 DispatchIO
。
在下面的例子中,DispatchIO
按照块读取文件,并通过调用 SHA256
类的 update 方法来处理每个块。
默认的块大小设置为 128 MB,以限制最大内存使用量。
import Foundation
import GoldenKey
final class FileHash {
private let workQueue: DispatchQueue
private let dispatchIO: DispatchIO
/// Opens and prepares a file for reading.
/// - Parameter fileURL: URL of the file.
/// - Parameter workQueue: DispatchQueue on which to perform work (read and calculating hash).
/// - Parameter queue: DispatchQueue of the completion handler.
/// - Parameter completion: Calls when the file closed. Useful when you want to calculate hash of multiple files sequentially.
init(
fileURL: URL,
workQueue: DispatchQueue = .init(label: "work", qos: .userInitiated),
queue: DispatchQueue = .main,
completion: (() -> Void)? = nil) throws {
self.workQueue = workQueue
let fileHandle = try FileHandle(forReadingFrom: fileURL)
dispatchIO = DispatchIO(
type: .stream,
fileDescriptor: fileHandle.fileDescriptor,
queue: queue,
cleanupHandler: { _ in
fileHandle.closeFile()
queue.async { completion?() }
}
)
dispatchIO.setLimit(lowWater: Int.max)
}
/// Calculates hash of the file
/// - Parameter hashFunctionType: Hash function type. SHA256.self for example.
/// - Parameter chunkSize: Max memory usage. 128 MB by default.
/// - Parameter queue: DispatchQueue of the completion handler.
/// - Parameter completion: Completion handler.
func calculateHash(
hashFunctionType: Digest.Type,
chunkSize: Int = 128 * 1024 * 1024,
queue: DispatchQueue = .main,
completion: @escaping (Result<Data, POSIXError>) -> Void) {
let hashFunction = hashFunctionType.init()
func readNextChunk() {
dispatchIO.read(offset: 0, length: chunkSize, queue: workQueue) { [weak self] (done, data, error) in
guard let self = self else { return }
guard error == 0 else {
let error = POSIXError(POSIXErrorCode(rawValue: error)!)
self.dispatchIO.close(flags: .stop)
queue.async {
completion(.failure(error))
}
return
}
guard let data = data else { return }
if data.isEmpty == false {
data.regions.forEach { hashFunction.update(data: $0) }
}
if done, data.isEmpty {
self.dispatchIO.close()
let digest = hashFunction.finalize()
queue.async {
completion(.success(digest))
}
}
if done, data.isEmpty == false {
readNextChunk()
}
}
}
readNextChunk()
}
}
extension Data {
/// Presents Data in hex format
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
do {
let fileURL = URL(string: "absolute_path_to_file")!
let fileHash = try FileHash(fileURL: fileURL)
fileHash.calculateHash(hashFunctionType: SHA256.self) { result in
switch result {
case .success(let hash):
print(hash.hexDescription)
case .failure(let error):
print(error.localizedDescription)
}
}
} catch (let error) {
print(error.localizedDescription)
}