Reputation: 3658
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?
Upvotes: 21
Views: 14103
Reputation: 754
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()
}
}
Upvotes: 11
Reputation: 11666
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
}
Upvotes: 3
Reputation: 540075
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.
Upvotes: 33
Reputation: 9935
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*/
Upvotes: 18
Reputation: 15778
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
).
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()
}
}
Upvotes: 7