【问题标题】:Get string md5 in Swift 5在 Swift 5 中获取字符串 md5
【发布时间】:2019-08-16 18:45:30
【问题描述】:

在 Swift 4 中我们可以使用

var md5: String? {
    guard let data = self.data(using: .utf8) else { return nil }
    let hash = data.withUnsafeBytes { (bytes: UnsafePointer<Data>) -> [UInt8] in
        var hash: [UInt8] = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        CC_MD5(bytes, CC_LONG(data.count), &hash)
        return hash
    }
    return hash.map { String(format: "%02x", $0) }.joined()
}

但在 Swift 5 中 withUnsafeBytes 使用 UnsafeRawBufferPointer 而不是 UnsafePointer。如何更改md5功能?

【问题讨论】:

  • 您可以使用 Swift 迁移助手从 Swift 4.2 到 5.0 来获得针对此类小段代码的解决方案。
  • 顺便说一句,你的 Swift 4 代码中的UnsafePointer&lt;Data&gt; 没有意义,它应该是UnsafePointer&lt;UInt8&gt;——它之所以有效,只是因为闭包不依赖于实际的指针类型。

标签: ios swift md5 swift5


【解决方案1】:

Swift 5 版本:使用 UnsafeRawBufferPointer 作为闭包参数的类型,使用 bytes.baseAddress 将地址传递给 Common Crypto 函数:

import Foundation
import CommonCrypto

extension String {
    var md5: String {
        let data = Data(self.utf8)
        let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
            var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
            CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
            return hash
        }
        return hash.map { String(format: "%02x", $0) }.joined()
    }
}

(注意字符串到UTF-8数据的转换不能失败,不需要返回可选项。)

iOS 13 已弃用 CC_MD5。您可以改用 CC_SHA256。

【讨论】:

  • 我必须添加 import var CommonCrypto.CC_MD5_DIGEST_LENGTH \nimport func CommonCrypto.CC_MD5 \nimport typealias CommonCrypto.CC_LONG 才能编译
  • @SamB:import CommonCrypto 应该也可以。
【解决方案2】:

爱斯基摩人的解决方案

以下是基于爱斯基摩人在 Swift 论坛帖子 withUnsafeBytes Data API confusion 中提出的解决方案的变体:

extension String {
    func md5() -> String {
        let data = Data(utf8)
        var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))

        data.withUnsafeBytes { buffer in
            _ = CC_MD5(buffer.baseAddress, CC_LONG(buffer.count), &hash)
        }

        return hash.map { String(format: "%02hhx", $0) }.joined()
    }
}

请注意,它实际上与 Martin R 的解决方案相同,但行更短(不是 return hash)。

使用 NSData 的解决方案

这是一个使用桥接到 NSData 的更短的解决方案。

extension String {
    func md5() -> String {
        let data = Data(utf8) as NSData
        var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        CC_MD5(data.bytes, CC_LONG(data.length), &hash)
        return hash.map { String(format: "%02hhx", $0) }.joined()
    }
}

【讨论】:

    【解决方案3】:

    在 iOS 13 及更高版本中,有一个框架 CryptoKit,它是 CommonCrypto 框架和 MD5 哈希函数的包装器。

    import CryptoKit
    
    let d = "Hello"
    let r = Insecure.MD5.hash(data: d.data(using: .utf8)!)
    print(r)
    
    /*Output: MD5 digest: 8b1a9953c4611296a827abf8c47804d7*/
    

    【讨论】:

    【解决方案4】:

    CC_MD5 回馈 'CC_MD5' 在 iOS 13.0 中已弃用:此功能在密码学上被破坏,不应在安全上下文中使用。客户端应迁移到 SHA256(或更高版本)。

    所以要有一个灵活的解决方案:

    //OLD
    import CommonCrypto
    
    //new:
    import CryptoKit
    
    extension String {
    var md5: String {
    
        if #available(iOS 13.0, *) {
    
            guard let d = self.data(using: .utf8) else { return ""}
            let digest = Insecure.MD5.hash(data: d)
            let h = digest.reduce("") { (res: String, element) in
                let hex = String(format: "%02x", element)
                //print(ch, hex)
                let  t = res + hex
                return t
            }
            return h
    
        } else {
            // Fall back to pre iOS13
            let length = Int(CC_MD5_DIGEST_LENGTH)
            var digest = [UInt8](repeating: 0, count: length)
            
            if let d = self.data(using: .utf8) {
                _ = d.withUnsafeBytes { body -> String in
                    CC_MD5(body.baseAddress, CC_LONG(d.count), &digest)
                    return ""
                }
            }
            let result = (0 ..< length).reduce("") {
                $0 + String(format: "%02x", digest[$1])
            }
            return result
    
        }// end of fall back
    
    }
    

    }

    测试:

    func MD5Test() -> Bool{
        let HASHED = "5D41402ABC4B2A76B9719D911017C592"
        let s = "hello"
        let md5_1 = s.md5
        if  md5_1.uppercased() != HASHED{
            return false
        }
        return true
    }
    

    【讨论】:

      【解决方案5】:

      在 iOS 13 及更高版本中有一个框架CryptoKit。试试这个:

      extension Data {       
          var md5: String {
              Insecure.MD5
                  .hash(data: self)
                  .map {String(format: "%02x", $0)}
                  .joined()
          }
      }
      

      【讨论】:

      • 用第三方库解决问题,用十行代码sn-p就可以解决的不好
      • @ValentinShamardin CryptoKit 不是第三方库。它是自 iOS 13 起可用的苹果框架
      猜你喜欢
      • 2011-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-18
      • 2021-07-08
      • 2019-02-02
      • 2021-04-16
      • 1970-01-01
      相关资源
      最近更新 更多