Swift 3中数据的MD5值

12

我想获取我的数据的MD5哈希值(从互联网上下载的图像)。不幸的是,我已经将框架升级到Swift 3,我之前使用的方法现在无法使用。

我已经把大部分代码转换了,但是我无法从数据中获取字节:

import Foundation
import CommonCrypto


struct MD5 {

    static func get(data: Data) -> String {
        var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        CC_MD5(data.bytes, CC_LONG(data.count), &digest)

        var digestHex = ""
        for index in 0..<Int(CC_MD5_DIGEST_LENGTH) {
            digestHex += String(format: "%02x", digest[index])
        }

        return digestHex
    }

}

CommonCrypto已经作为自定义模块导入。问题在于,我得到了'bytes'不可用:请改用withUnsafeBytes 的错误提示,出现在 CC_MD5(data.bytes,... 中。

因此,真正的问题是,如何将字节从数据中获取,并且这个解决方案是否可行?


这是我的纯Swift实现,不需要CommonCrypto。链接 - Nikolai Ruhe
2个回答

17
    CC_MD5(data.bytes, CC_LONG(data.count), &digest)

如前所述,bytes不可用,因为它很危险。它是指向内存的原始指针,可能会消失。建议的解决方案是使用withUnsafeBytes,该函数承诺在指针的作用域期间目标不会消失。记忆中,它看起来应该像这样:

data.withUnsafeBytes { bytes in
    CC_MD5(bytes, CC_LONG(data.count), &digest)
}

关键在于 bytes 指针不能逃逸到 data 不再有效的作用域中。

关于这一点,可以参考使用类似 MD5CCHmac 的例子,可以在 RNCryptor 中查看。


我寻找这个答案已经太久了!谢谢你。 - Sn0wfreeze
Swift是我使用过的最糟糕的语言之一的另一个原因:为什么不直接使用md5(blah:AnyObject) -> String :(? - Nick M
1
你提出的语法极其模糊,会导致很大的混淆(这与我经常遇到的那些试图在PHP和JavaScript中执行哈希并得到意外和不正确结果的开发人员所遇到的混淆相同,我必须指导他们修复)。但是,如果这不是从C自动桥接过来的,并且有一个适当的Swift接口,那么你是正确的,它永远不应该看起来像这样(因为这个自动桥接的语法非常丑陋)。它将成为Data上的一个简单方法,并返回一个Data。可能是Data.md5Hashed()或类似的东西。 - Rob Napier

5
这是一个一行代码的示例:
import CryptoKit
let md5String = Insecure.MD5.hash(data: data).map { String(format: "%02hhx", $0) }.joined()

对于任何感兴趣的人,这里有一个示例,您可以在其基础上构建以支持不同的算法:

用法:

Checksum.hash(data: data, using: .md5) == "MyMD5Hash"

代码片段:

import Foundation
import CommonCrypto

struct Checksum {
    private init() {}

    static func hash(data: Data, using algorithm: HashAlgorithm) -> String {
        /// Creates an array of unsigned 8 bit integers that contains zeros equal in amount to the digest length
        var digest = [UInt8](repeating: 0, count: algorithm.digestLength())

        /// Call corresponding digest calculation
        data.withUnsafeBytes {
            algorithm.digestCalculation(data: $0.baseAddress, len: UInt32(data.count), digestArray: &digest)
        }

        var hashString = ""
        /// Unpack each byte in the digest array and add them to the hashString
        for byte in digest {
            hashString += String(format:"%02x", UInt8(byte))
        }

        return hashString
    }

    /**
    * Hash using CommonCrypto
    * API exposed from CommonCrypto-60118.50.1:
    * https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60118.50.1/include/CommonDigest.h.auto.html
    **/
    enum HashAlgorithm {
        case md5
        case sha256

        func digestLength() -> Int {
            switch self {
            case .md5:
                return Int(CC_MD5_DIGEST_LENGTH)
            case .sha256:
                return Int(CC_SHA256_DIGEST_LENGTH)
            }
        }

        /// CC_[HashAlgorithm] performs a digest calculation and places the result in the caller-supplied buffer for digest
        /// Calls the given closure with a pointer to the underlying unsafe bytes of the data's contiguous storage.
        func digestCalculation(data: UnsafeRawPointer!, len: UInt32, digestArray: UnsafeMutablePointer<UInt8>!) {
            switch self {
            case .md5:
                CC_MD5(data, len, digestArray)
            case .sha256:
                CC_SHA256(data, len, digestArray)
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接