Get string md5 in Swift 5
Asked Answered
C

5

21

In Swift 4 we could use

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()
}

But in Swift 5 withUnsafeBytes uses UnsafeRawBufferPointer instead of UnsafePointer. How to change md5 function?

Calamanco answered 26/3, 2019 at 11:37 Comment(2)
You may use Swift migration assistant from Swift 4.2 to 5.0 to get a solution for small pieces of code like that.Paramatta
Btw, UnsafePointer<Data> in your Swift 4 code makes no sense, it should be UnsafePointer<UInt8> – it works only because the closure does not depend on the actual pointer type.Oxen
O
33

Swift 5 version: Use UnsafeRawBufferPointer as type of the closure argument, and bytes.baseAddress to pass address to the Common Crypto function:

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()
    }
}

(Note that the conversion of a string to UTF-8 data cannot fail, there is no need to return an optional.)

CC_MD5 has been deprecated with the iOS 13. Instead, you can use CC_SHA256.

Oxen answered 26/3, 2019 at 12:4 Comment(2)
I had to add import var CommonCrypto.CC_MD5_DIGEST_LENGTH \nimport func CommonCrypto.CC_MD5 \nimport typealias CommonCrypto.CC_LONG to get this to compileTraumatize
@SamB: Just import CommonCrypto should work as well.Oxen
D
17

In iOS 13 and above there is a framework CryptoKit which is a wrapper around CommonCrypto framework and around the MD5 hash function.

import CryptoKit

let d = "Hello"
let r = Insecure.MD5.hash(data: d.data(using: .utf8)!)
print(r)

/*Output: MD5 digest: 8b1a9953c4611296a827abf8c47804d7*/
Dicotyledon answered 28/2, 2020 at 12:42 Comment(1)
Also available in macOS 10.15+; for Linux, you may use github.com/apple/swift-crypto.Bohun
R
11

In iOS 13 and above there is a framework CryptoKit. Try using this:

extension Data {       
    var md5: String {
        Insecure.MD5
            .hash(data: self)
            .map {String(format: "%02x", $0)}
            .joined()
    }
}
Repertoire answered 3/8, 2021 at 19:46 Comment(2)
It's no good to use third-party libraries to solve problems that can be solved with a ten lined code snippetCalamanco
@ValentinShamardin CryptoKit is not third-party library. It is apples framework available since iOS 13Repertoire
U
7

Eskimo's solution

Below is a variant based on a solution proposed by Eskimo from Apple in Swift Forum post 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()
    }
}

Note it is effectively the same as Martin R's solution, but a line shorter (no return hash).

Solution using NSData

This is an even shorter solution using bridging to 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()
    }
}
Underfeed answered 2/4, 2019 at 8:31 Comment(0)
Z
2

CC_MD5 gives back 'CC_MD5' was deprecated in iOS 13.0: This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).

so to have a flexible solution:

//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

}

}

to test:

func MD5Test() -> Bool{
    let HASHED = "5D41402ABC4B2A76B9719D911017C592"
    let s = "hello"
    let md5_1 = s.md5
    if  md5_1.uppercased() != HASHED{
        return false
    }
    return true
}
Zaslow answered 25/4, 2021 at 10:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.