144

I want to convert a string like "abc" to an MD5 hash. I want to do this in iOS and Swift. I have tried using the solutions below but they were not working for me:

Importing CommonCrypto in a Swift framework

How to use CC_MD5 method in swift language.

http://iosdeveloperzone.com/2014/10/03/using-commoncrypto-in-swift/

To be more clear, I want to achieve an output in Swift similar to this PHP code's output:

$str = "Hello";

echo md5($str);

Output: 8b1a9953c4611296a827abf8c47804d7

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
user3606682
  • 1,945
  • 3
  • 17
  • 24
  • 5
    What's wrong with the links you gave? – jtbandes Aug 23 '15 at 06:30
  • 2
    The links you gave should work. Can you describe what your exact problem is? You could also include a third-party library to do what you want, ie. https://github.com/krzyzanowskim/CryptoSwift – Eric Amorde Aug 23 '15 at 06:34
  • 1
    As I have mentioned that im new to swift programing i was confused to implement it in right way. i was including this file(#import ) in swift controller file. But thanks for your replies, it is resolved now by Mr.zaph answer given below. – user3606682 Aug 24 '15 at 12:43
  • If you want a home grown implementation in Swift, then https://github.com/onmyway133/SwiftHash – onmyway133 Jul 02 '16 at 17:09

19 Answers19

201

There are two steps:
1. Create md5 data from a string
2. Covert the md5 data to a hex string

Swift 2.0:

func md5(string string: String) -> String {
    var digest = [UInt8](count: Int(CC_MD5_DIGEST_LENGTH), repeatedValue: 0)
    if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
        CC_MD5(data.bytes, CC_LONG(data.length), &digest)
    }

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

    return digestHex
}

//Test:
let digest = md5(string:"Hello")
print("digest: \(digest)")

Output:

digest: 8b1a9953c4611296a827abf8c47804d7

Swift 3.0:

func MD5(string: String) -> Data {
    let messageData = string.data(using:.utf8)!
    var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))

    _ = digestData.withUnsafeMutableBytes {digestBytes in
        messageData.withUnsafeBytes {messageBytes in
            CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
        }
    }

    return digestData
}

//Test:
let md5Data = MD5(string:"Hello")

let md5Hex =  md5Data.map { String(format: "%02hhx", $0) }.joined()
print("md5Hex: \(md5Hex)")

let md5Base64 = md5Data.base64EncodedString()
print("md5Base64: \(md5Base64)")

Output:

md5Hex: 8b1a9953c4611296a827abf8c47804d7
md5Base64: ixqZU8RhEpaoJ6v4xHgE1w==

Swift 5.0:

import Foundation
import var CommonCrypto.CC_MD5_DIGEST_LENGTH
import func CommonCrypto.CC_MD5
import typealias CommonCrypto.CC_LONG

func MD5(string: String) -> Data {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        let messageData = string.data(using:.utf8)!
        var digestData = Data(count: length)

        _ = digestData.withUnsafeMutableBytes { digestBytes -> UInt8 in
            messageData.withUnsafeBytes { messageBytes -> UInt8 in
                if let messageBytesBaseAddress = messageBytes.baseAddress, let digestBytesBlindMemory = digestBytes.bindMemory(to: UInt8.self).baseAddress {
                    let messageLength = CC_LONG(messageData.count)
                    CC_MD5(messageBytesBaseAddress, messageLength, digestBytesBlindMemory)
                }
                return 0
            }
        }
        return digestData
    }

//Test:
let md5Data = MD5(string:"Hello")

let md5Hex =  md5Data.map { String(format: "%02hhx", $0) }.joined()
print("md5Hex: \(md5Hex)")

let md5Base64 = md5Data.base64EncodedString()
print("md5Base64: \(md5Base64)")

Output:

md5Hex: 8b1a9953c4611296a827abf8c47804d7
md5Base64: ixqZU8RhEpaoJ6v4xHgE1w==

Notes:
#import <CommonCrypto/CommonCrypto.h> must be added to a Bridging-Header file

For how to create a Bridging-Header see this SO answer.

In general MD5 should not be used for new work, SHA256 is a current best practice.

Example from deprecated documentation section:

MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3+)

These functions will hash either String or Data input with one of eight cryptographic hash algorithms.

The name parameter specifies the hash function name as a String
Supported functions are MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 a This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.



This function takes a hash name and String to be hashed and returns a Data:

name: A name of a hash function as a String  
string: The String to be hashed  
returns: the hashed result as Data  
func hash(name:String, string:String) -> Data? {
    let data = string.data(using:.utf8)!
    return hash(name:name, data:data)
}

Examples:

let clearString = "clearData0123456"
let clearData   = clearString.data(using:.utf8)!
print("clearString: \(clearString)")
print("clearData: \(clearData as NSData)")

let hashSHA256 = hash(name:"SHA256", string:clearString)
print("hashSHA256: \(hashSHA256! as NSData)")

let hashMD5 = hash(name:"MD5", data:clearData)
print("hashMD5: \(hashMD5! as NSData)")

Output:

clearString: clearData0123456
clearData: <636c6561 72446174 61303132 33343536>

hashSHA256: <aabc766b 6b357564 e41f4f91 2d494bcc bfa16924 b574abbd ba9e3e9d a0c8920a>
hashMD5: <4df665f7 b94aea69 695b0e7b baf9e9d6>
Repose
  • 2,157
  • 1
  • 24
  • 26
zaph
  • 111,848
  • 21
  • 189
  • 228
  • 3
    Thanks alottt @zaph, I was struggling for this since more than 2 days. Got it resolved with your above answer :) And yes im retrieving old data from the web where MD5 is used, so im forced to use MD5. But thanks again for the answer and suggestion to use SHA256 :) – user3606682 Aug 24 '15 at 12:32
  • `String(data: digestData, encoding: String.Encoding.utf8)` throws `fatal error: unexpectedly found nil while unwrapping an Optional value` – Siddharth Apr 27 '17 at 04:52
  • @Siddharth There is not enough information in the comment, it is not clear what `digestData` is. If it is hash data the chances or it being UTF-8 (or any string encoding is slim to nonexistent. – zaph Apr 30 '17 at 02:21
  • @zaph I have used the code as it is, and I am getting an error. – Siddharth Apr 30 '17 at 06:14
  • @Siddharth Hash data is binary data and the chances or it being UTF-8 (or any string encoding) is slim to nonexistent. If you need a `String` representation the usual method is to encode the data as either hexadecimal or Base64. See the test examples; for hexadecimal: `md5Hex`, for Base64:`md5Base64` (just added). – zaph Apr 30 '17 at 14:40
  • how can you recompute the MD5 hash!? – Learn2Code May 01 '17 at 02:58
  • @Learn2Code I don't really understand but for the same input you get the same output. If that does not help please e more specific in what you mean. – zaph May 01 '17 at 04:39
  • @zaph Heads up - we're down to very few links to Docs remaining in Q&A and your posts are on that list. We need to keep going with the shutdown and cleanup, and one of the next steps is redirecting all links to a static landing page. Current plan is to make that switch next Monday, on the 9th, so if you have some time to make any edits you think are best to your remaining posts that have links to Documentation, that'd be very helpful. Thanks! – Adam Lear Oct 03 '17 at 21:14
  • 1
    Here's how you can improve it: import only required symbols and not the entire CommonCrypto, because it's a bit of an overhead otherwise: import var CommonCrypto.CC_MD5_DIGEST_LENGTH import func CommonCrypto.CC_MD5 import typealias CommonCrypto.CC_LONG – Igor Vasilev Feb 13 '19 at 03:31
  • 3
    @zaph you may want to add the iOS 13 CryptoKit solution to your answer which I detailed in my answer below: https://stackoverflow.com/a/56578995/368085 – mluisbrown Jun 13 '19 at 11:03
  • @mluisbrown You already added it, great! But: CryptoKit is so lacking it is pathetic, it is as if Apple does not care about crypto. – zaph Jun 18 '19 at 19:11
  • You don't want to do "%02hhx", since there are many high ASCII characters that are 3 byte. Make sure that you test with high ASCII chars like Cyrillic. – Alex Zavatone Aug 12 '21 at 16:15
118

As of iOS 13 Apple has added the CryptoKit framework so you no longer need to import CommonCrypto or deal with its C API:

import Foundation
import CryptoKit

func MD5(string: String) -> String {
    let digest = Insecure.MD5.hash(data: Data(string.utf8))

    return digest.map {
        String(format: "%02hhx", $0)
    }.joined()
}
 
eastriver lee
  • 173
  • 2
  • 9
mluisbrown
  • 14,448
  • 7
  • 58
  • 86
  • 4
    It's also worth noting that this provides a means of avoiding the warning about MD5 now being insecure. You don't need to implement CommonCrypto in Objective-C so that you have support for pragmas to disable the warning. Handy if you are working in an environment that puts an emphasis on dealing with warnings. – marcus.ramsden Jan 14 '20 at 08:51
  • This is, by far, the easiest solution I've found. But why doesn't Insecure.MD5.Digest already have a function (or property) to do this? – NRitH Mar 26 '21 at 04:18
  • 2
    Note that to convert the digest to base64-encoded format instead of hex format, change the return statement to `return Data(digest).base64EncodedString()` – wristbands Nov 23 '21 at 04:22
52

After reading through the other answers on here (and needing to support other hash types as well) I wrote a String extension that handles multiple hash types and output types.

NOTE: CommonCrypto is included in Xcode 10, so you can simply import CommonCrypto without having to mess with a bridging header if you have the latest Xcode version installed... Otherwise a bridging header is necessary.


UPDATE: Both Swift 4 & 5 use the same String+Crypto.swift file below.

There is a separate Data+Crypto.swift file for Swift 5 (see below) as the api for 'withUnsafeMutableBytes' and 'withUnsafeBytes' changed between Swift 4 & 5.


String+Crypto.swift -- (for both Swift 4 & 5)

import Foundation
import CommonCrypto

// Defines types of hash string outputs available
public enum HashOutputType {
    // standard hex string output
    case hex
    // base 64 encoded string output
    case base64
}

// Defines types of hash algorithms available
public enum HashType {
    case md5
    case sha1
    case sha224
    case sha256
    case sha384
    case sha512

    var length: Int32 {
        switch self {
        case .md5: return CC_MD5_DIGEST_LENGTH
        case .sha1: return CC_SHA1_DIGEST_LENGTH
        case .sha224: return CC_SHA224_DIGEST_LENGTH
        case .sha256: return CC_SHA256_DIGEST_LENGTH
        case .sha384: return CC_SHA384_DIGEST_LENGTH
        case .sha512: return CC_SHA512_DIGEST_LENGTH
        }
    }
}

public extension String {

    /// Hashing algorithm for hashing a string instance.
    ///
    /// - Parameters:
    ///   - type: The type of hash to use.
    ///   - output: The type of output desired, defaults to .hex.
    /// - Returns: The requested hash output or nil if failure.
    public func hashed(_ type: HashType, output: HashOutputType = .hex) -> String? {

        // convert string to utf8 encoded data
        guard let message = data(using: .utf8) else { return nil }
        return message.hashed(type, output: output)
    } 
}

SWIFT 5 -- Data+Crypto.swift

import Foundation
import CommonCrypto

extension Data {

    /// Hashing algorithm that prepends an RSA2048ASN1Header to the beginning of the data being hashed.
    ///
    /// - Parameters:
    ///   - type: The type of hash algorithm to use for the hashing operation.
    ///   - output: The type of output string desired.
    /// - Returns: A hash string using the specified hashing algorithm, or nil.
    public func hashWithRSA2048Asn1Header(_ type: HashType, output: HashOutputType = .hex) -> String? {

        let rsa2048Asn1Header:[UInt8] = [
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
            0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
        ]

        var headerData = Data(rsa2048Asn1Header)
        headerData.append(self)

        return hashed(type, output: output)
    }

    /// Hashing algorithm for hashing a Data instance.
    ///
    /// - Parameters:
    ///   - type: The type of hash to use.
    ///   - output: The type of hash output desired, defaults to .hex.
    ///   - Returns: The requested hash output or nil if failure.
    public func hashed(_ type: HashType, output: HashOutputType = .hex) -> String? {

        // setup data variable to hold hashed value
        var digest = Data(count: Int(type.length))

        _ = digest.withUnsafeMutableBytes{ digestBytes -> UInt8 in
            self.withUnsafeBytes { messageBytes -> UInt8 in
                if let mb = messageBytes.baseAddress, let db = digestBytes.bindMemory(to: UInt8.self).baseAddress {
                    let length = CC_LONG(self.count)
                    switch type {
                    case .md5: CC_MD5(mb, length, db)
                    case .sha1: CC_SHA1(mb, length, db)
                    case .sha224: CC_SHA224(mb, length, db)
                    case .sha256: CC_SHA256(mb, length, db)
                    case .sha384: CC_SHA384(mb, length, db)
                    case .sha512: CC_SHA512(mb, length, db)
                    }
                }
                return 0
            }
        }

        // return the value based on the specified output type.
        switch output {
        case .hex: return digest.map { String(format: "%02hhx", $0) }.joined()
        case .base64: return digest.base64EncodedString()
        }
    }
}

SWIFT 4 -- Data+Crypto.swift

import Foundation
import CommonCrypto 

extension Data {

    /// Hashing algorithm that prepends an RSA2048ASN1Header to the beginning of the data being hashed.
    ///
    /// - Parameters:
    ///   - type: The type of hash algorithm to use for the hashing operation.
    ///   - output: The type of output string desired.
    /// - Returns: A hash string using the specified hashing algorithm, or nil.
    public func hashWithRSA2048Asn1Header(_ type: HashType, output: HashOutputType = .hex) -> String? {

        let rsa2048Asn1Header:[UInt8] = [
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
            0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
        ]

        var headerData = Data(bytes: rsa2048Asn1Header)
        headerData.append(self)

        return hashed(type, output: output)
    }

    /// Hashing algorithm for hashing a Data instance.
    ///
    /// - Parameters:
    ///   - type: The type of hash to use.
    ///   - output: The type of hash output desired, defaults to .hex.
    ///   - Returns: The requested hash output or nil if failure.
    public func hashed(_ type: HashType, output: HashOutputType = .hex) -> String? {

        // setup data variable to hold hashed value
        var digest = Data(count: Int(type.length))

        // generate hash using specified hash type
        _ = digest.withUnsafeMutableBytes { (digestBytes: UnsafeMutablePointer<UInt8>) in
            self.withUnsafeBytes { (messageBytes: UnsafePointer<UInt8>) in
                let length = CC_LONG(self.count)
                switch type {
                case .md5: CC_MD5(messageBytes, length, digestBytes)
                case .sha1: CC_SHA1(messageBytes, length, digestBytes)
                case .sha224: CC_SHA224(messageBytes, length, digestBytes)
                case .sha256: CC_SHA256(messageBytes, length, digestBytes)
                case .sha384: CC_SHA384(messageBytes, length, digestBytes)
                case .sha512: CC_SHA512(messageBytes, length, digestBytes)
                }
            }
        }

        // return the value based on the specified output type.
        switch output {
        case .hex: return digest.map { String(format: "%02hhx", $0) }.joined()
        case .base64: return digest.base64EncodedString()
        }
    }
}

Edit: since the hash actually happens on the Data, I split the hashing algorithm out into a Data extension. This allows the same algorithm to be used for SSL Certificate pinning hash operations as well.

Here's a short example of how you might use it for an SSL Pinning operation:

// Certificate pinning - get certificate as data
let data: Data = SecCertificateCopyData(serverCertificate) as Data

// compare hash of server certificate with local (expected) hash value
guard let serverHash = data.hashWithRSA2048Asn1Header(.sha256, output: .base64), serverHash == storedHash else {
    print("SSL PINNING: Server certificate hash does not match specified hash value.")
    return false
}

back to the original answer

I tested the hash algorithms using this:

let value = "This is my string"

if let md5 = value.hashed(.md5) {
    print("md5: \(md5)")
}
if let sha1 = value.hashed(.sha1) {
    print("sha1: \(sha1)")
}
if let sha224 = value.hashed(.sha224) {
    print("sha224: \(sha224)")
}
if let sha256 = value.hashed(.sha256) {
    print("sha256: \(sha256)")
}
if let sha384 = value.hashed(.sha384) {
    print("sha384: \(sha384)")
}
if let sha512 = value.hashed(.sha512) {
    print("sha512: \(sha512)")
}

and this is the printed results:

md5: c2a9ce57e8df081b4baad80d81868bbb
sha1: 37fb219bf98bee51d2fdc3ba6d866c97f06c8223
sha224: f88e2f20aa89fb4dffb6bdc62d7bd75e1ba02574fae4a437c3bf49c7
sha256: 9da6c02379110815278b615f015f0b254fd3d5a691c9d8abf8141655982c046b
sha384: d9d7fc8aefe7f8f0a969b132a59070836397147338e454acc6e65ca616099d03a61fcf9cc8c4d45a2623145ebd398450
sha512: 349cc35836ba85915ace9d7f895b712fe018452bb4b20ff257257e12adeb1e83ad780c6568a12d03f5b2cb1e3da23b8b7ced9012a188ef3855e0a8f3db211883
digitalHound
  • 4,384
  • 27
  • 27
28

SWIFT 3 version of md5 function:

func md5(_ string: String) -> String {

    let context = UnsafeMutablePointer<CC_MD5_CTX>.allocate(capacity: 1)
    var digest = Array<UInt8>(repeating:0, count:Int(CC_MD5_DIGEST_LENGTH))
    CC_MD5_Init(context)
    CC_MD5_Update(context, string, CC_LONG(string.lengthOfBytes(using: String.Encoding.utf8)))
    CC_MD5_Final(&digest, context)
    context.deallocate(capacity: 1)
    var hexString = ""
    for byte in digest {
        hexString += String(format:"%02x", byte)
    }

    return hexString
}

Original link from http://iosdeveloperzone.com

Wajih
  • 4,227
  • 2
  • 25
  • 40
26

Swift 4.* , Xcode 10 Update :

In Xcode 10 you don't have to use Bridging-Header Anymore , you can directly import using

import CommonCrypto

And Then write a method something like :

func MD5(_ string: String) -> String? {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        var digest = [UInt8](repeating: 0, count: length)

        if let d = string.data(using: String.Encoding.utf8) {
            _ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
                CC_MD5(body, CC_LONG(d.count), &digest)
            }
        }

        return (0..<length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }

Usage :

MD5("This is my string")

Output:

c2a9ce57e8df081b4baad80d81868bbb
Invictus Cody
  • 517
  • 6
  • 17
  • your solution is perfectly worked. Can we add SALT value with this MD5 encryption? I want add while encryption of string. can you provide some use-full link? – Punita Jan 28 '19 at 13:21
  • I am not sure what are you trying to achieve. Use "AES128" , if you want custom encryption with salting . if security of is your concern , Check this out : https://stackoverflow.com/a/15775071/3118377 . – Invictus Cody Jan 29 '19 at 06:33
  • Thanks Invictus Cody, I have concatenate SALT with String and able to get MD5. – Punita Feb 05 '19 at 11:33
  • Works great. But how do you convert it back to String? – DocAsh59 Feb 13 '19 at 14:57
  • You can't convert MD5 string back to string. Use AES instead. – Invictus Cody Feb 24 '19 at 05:19
  • @InvictusCody can you update your answer to Swift 5? 'withUnsafeBytes' was deprecated: use 'withUnsafeBytes(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead – J. Martin Mar 31 '19 at 00:59
  • 1
    Swift 5: `func MD5(_ string: String) -> String? { let length = Int(CC_MD5_DIGEST_LENGTH) var digest = [UInt8](repeating: 0, count: length) if let d = string.data(using: .utf8) { _ = d.withUnsafeBytes { body -> String in CC_MD5(body.baseAddress, CC_LONG(d.count), &digest) return "" } } return (0.. – Jim B May 15 '19 at 15:42
  • thanks @InvictusCody and JimB, this should be the accepted answer! – Radu Ursache Jun 04 '19 at 11:51
25

in Swift 5.3 you can do

import CryptoKit

func md5Hash(_ source: String) -> String {
    return Insecure.MD5.hash(data: source.data(using: .utf8)!).map { String(format: "%02hhx", $0) }.joined()
}
Radu Ursache
  • 1,230
  • 1
  • 22
  • 35
21

I released a pure Swift implementation that does not depend on CommonCrypto or anything else. It's available under MIT license.

The code consists of a single swift file that you can just drop into your project. If you prefer you can also use the contained Xcode project with framework and unit test targets.

It's simple to use:

let input = "The quick brown fox jumps over the lazy dog"
let digest = input.utf8.md5
print("md5: \(digest)")

prints: md5: 9e107d9d372bb6826bd81d3542a419d6

The swift file contains documentation and more examples.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • 4
    Requires Swift 4 which is not mentioned here or on the Github ReadMe. Usage should not be considered without performance figures provided in comparison to Common Crypto. Note: Common Crypto is FIPS 140 certified, SwiftDigest is not. Here is the key question: How is this better than Common Crypto for the implementation? More secure: No, faster: No. – zaph Sep 21 '17 at 03:20
  • 1
    @zaph The main purpose is to have an md5 implementation that does not depend on CommonCrypto. That's helpful in situations where CommonCrypto is not available—like Swift framework targets or on non-Apple platforms. – Nikolai Ruhe Sep 21 '17 at 06:42
  • You need to fully disclose the up and down sides, the reasons to and not to use it. Users need to know the performance and security tradeoff so they can make an informed choice. I am aware of the problems with Swift and Common Crypto I have personally talked with the security team at Apple that has this responsibility, and there is in-fighting between the Common Crypto and Swift teams. I have also been told that Common Crypto is not going to be updated, it is a dead end and there is no information on Apple's future course. But Common Crypto is still the fastest and secure solution. – zaph Sep 21 '17 at 10:39
  • 4
    @zaph I agree that security relevant implementations are not to be taken lightly. But MD5 has other uses than security—or, rather, security is where MD5 performs worst. Hashing algorithms are used for identification, sorting, storage, dictionaries, error detection and other reasons. MD5 is especially useful because of its ubiquity. So, while I agree with a couple of your comments I do not agree with the gist. I think your point of view and arguing is too narrow; it does not encompass the whole of the topic. – Nikolai Ruhe Sep 21 '17 at 12:04
  • 2
    Also, I just tested, and my implementation is faster than CommonCrypto for big messages :) – Nikolai Ruhe Sep 21 '17 at 12:06
  • Add some benchmarks in the ReadMe. State it is requires Swift 4. Be proactive in the Readme plusses and minuses. I can't test at the moment, I am having trouble with Xcode 9/OS 11 and will be traveling for several weeks. – zaph Sep 21 '17 at 12:15
  • `It's a single file' Where is this single file. The github has 100's of files. Which of these files should we use ? – Siddharth Feb 07 '18 at 12:03
  • 2
    I like this implementation. Thanks very much @NikolaiRuhe! I was able to convert it easily to Swift 3 compatibility. I also added a few convenience methods including computing digest of file contents given a URL, and retrieving the base64 encoding (useful for Content-MD5 among other things). @Siddharth the only file you need is MD5Digest.swift. – biomiker Feb 10 '18 at 09:59
  • Thanks, I'll take it and start trying again. – Siddharth Feb 10 '18 at 12:51
10

Just two notes here:

Using Crypto is too much overhead for achieving just this.

The accepted answer is perfect! Nevertheless I just wanted to share a Swift ier code approach using Swift 2.2.

Please bear in mind that you still have to #import <CommonCrypto/CommonCrypto.h> in your Bridging-Header file

struct MD5Digester {
    // return MD5 digest of string provided
    static func digest(string: String) -> String? {

        guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else { return nil }

        var digest = [UInt8](count: Int(CC_MD5_DIGEST_LENGTH), repeatedValue: 0)

        CC_MD5(data.bytes, CC_LONG(data.length), &digest)

        return (0..<Int(CC_MD5_DIGEST_LENGTH)).reduce("") { $0 + String(format: "%02x", digest[$1]) }
    }
}
Community
  • 1
  • 1
Hugo Alonso
  • 6,684
  • 2
  • 34
  • 65
9

Swift 5 answer as a String extension (based on the great answer of Invictus Cody):

import CommonCrypto

extension String {
    var md5Value: String {
        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 ""
            }
        }

        return (0 ..< length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }
}

Usage:

print("test".md5Value) /*098f6bcd4621d373cade4e832627b4f6*/
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
6

Here's an extension based on zaph answer

extension String{
    var MD5:String {
        get{
            let messageData = self.data(using:.utf8)!
            var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))

            _ = digestData.withUnsafeMutableBytes {digestBytes in
                messageData.withUnsafeBytes {messageBytes in
                    CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
                }
            }

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

Fully compatible with swift 3.0.you still have to #import <CommonCrypto/CommonCrypto.h> in your Bridging-Header file

4

In swift programming its better to make a string function, so the use will be easy. Here I am making a String extension using one of the above given solution. Thanks @wajih

import Foundation
import CommonCrypto

extension String {

func md5() -> String {

    let context = UnsafeMutablePointer<CC_MD5_CTX>.allocate(capacity: 1)
    var digest = Array<UInt8>(repeating:0, count:Int(CC_MD5_DIGEST_LENGTH))
    CC_MD5_Init(context)
    CC_MD5_Update(context, self, CC_LONG(self.lengthOfBytes(using: String.Encoding.utf8)))
    CC_MD5_Final(&digest, context)
    context.deallocate()
    var hexString = ""
    for byte in digest {
        hexString += String(format:"%02x", byte)
    }

    return hexString
}
}

Usage

let md5String = "abc".md5()
Rahul K Rajan
  • 776
  • 10
  • 19
4

An answer for Swift 5 with proper memory management and without String class inside the method:

typealias CBridgeCryptoMethodType = (UnsafeRawPointer?,
                                 UInt32,
                                 UnsafeMutablePointer<UInt8>?)
-> UnsafeMutablePointer<UInt8>?

private enum HashType {

    // MARK: - Cases

    case md5
    case sha1
    case sha224
    case sha256
    case sha384
    case sha512
}

extension Data {
    var hexString: String {
        let localHexString = reduce("", { previous, current in
            return previous + String(format: "%02X", current)
        })
        return localHexString
    }
    var md5: Data {
        return hashed(for: .md5)
    }
    var sha1: Data {
        return hashed(for: .sha1)
    }
    var sha224: Data {
        return hashed(for: .sha224)
    }
    var sha256: Data {
        return hashed(for: .sha256)
    }
    var sha384: Data {
        return hashed(for: .sha384)
    }
    var sha512: Data {
        return hashed(for: .sha512)
    }

    private func hashed(for hashType: HashType) -> Data {
        return withUnsafeBytes { (rawBytesPointer: UnsafeRawBufferPointer) -> Data in
            guard let bytes = rawBytesPointer.baseAddress?.assumingMemoryBound(to: Float.self) else {
                return Data()
            }
            let hashMethod: CBridgeCryptoMethodType
            let digestLength: Int
            switch hashType {
            case .md5:
                hashMethod = CC_MD5
                digestLength = Int(CC_MD5_DIGEST_LENGTH)
            case .sha1:
                hashMethod = CC_SHA1
                digestLength = Int(CC_SHA1_DIGEST_LENGTH)
            case .sha224:
                hashMethod = CC_SHA224
                digestLength = Int(CC_SHA224_DIGEST_LENGTH)
            case .sha256:
                hashMethod = CC_SHA256
                digestLength = Int(CC_SHA256_DIGEST_LENGTH)
            case .sha384:
                hashMethod = CC_SHA384
                digestLength = Int(CC_SHA384_DIGEST_LENGTH)
            case .sha512:
                hashMethod = CC_SHA512
                digestLength = Int(CC_SHA512_DIGEST_LENGTH)
            }
            let result = UnsafeMutablePointer<UInt8>.allocate(capacity: digestLength)
            _ = hashMethod(bytes, CC_LONG(count), result)
            let md5Data = Data(bytes: result, count: digestLength)
            result.deallocate()
            return md5Data
        }
    }
}

example

let str = "The most secure string ever"
print("md5", str.data(using: .utf8)?.md5.hexString)
print("sha1", str.data(using: .utf8)?.sha1.hexString)
print("sha224", str.data(using: .utf8)?.sha224.hexString)
print("sha256", str.data(using: .utf8)?.sha256.hexString)
print("sha384", str.data(using: .utf8)?.sha384.hexString)
print("sha512", str.data(using: .utf8)?.sha512.hexString)

Results:

md5 Optional("671C121427F12FBBA66CEE71C44CB62C")

sha1 Optional("A6A40B223AE634CFC8C191DDE024BF0ACA56D7FA")

sha224 Optional("334370E82F2F5ECF5B2CA0910C6176D94CBA12FD6F518A7AB8D12ADE")

sha256 Optional("8CF5ED971D6EE2579B1BDEFD4921415AC03DA45B49B89665B3DF197287EFC89D")

sha384 Optional("04BB3551CBD60035BA7E0BAA141AEACE1EF5E17317A8FD108DA12A7A8E98C245E14F92CC1A241C732209EAC9D600602E")

sha512 Optional("1D595EAFEB2162672830885D336F75FD481548AC463BE16A8D98DB33637213F1AEB36FA4977B9C23A82A4FAB8A70C06AFC64C610D3CB1FE77A609DC8EE86AA68")

Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
4

Swift 5.3: Building on @Radu Ursache and @mluisbrown answers: a simple extension on String

import CryptoKit

extension String {
    func md5() -> String {
        Insecure.MD5.hash(data: self.data(using: .utf8)!).map { String(format: "%02hhx", $0) }.joined()
    }
}

Usage:

"My awesome String".md5()

// edit: By now I would prefer a computed property over a function without paramters.

import CryptoKit

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

Usage:

"My awesome String".md5
palme
  • 2,499
  • 2
  • 21
  • 38
3
import CryptoKit
private func MD5Base64(_ string: String) -> String {
    let digest = Insecure.MD5.hash(data: string.data(using: .utf8) ?? Data())
    let digestString = digest.map { String(format: "%02hhx", $0) }.joined()
    return digestString.toBase64()
}

extension String {
    func toBase64() -> String {
        return Data(self.utf8).base64EncodedString()
    }
}
siki
  • 33
  • 1
  • 4
1

I used Carthage and Cyrpto to do this.

  1. Install Carthage if you've not already done so

  2. Install Crypto into your project

  3. execute 'cartage update'

  4. If you're running from the commandline add in the framework in the swift file

    #!/usr/bin/env xcrun swift -F Carthage/Build/Mac
    
  5. Add import Crypto to your swift file.

  6. then it just works!

    print( "convert this".MD5 )
    
Keith John Hutchison
  • 4,955
  • 11
  • 46
  • 64
  • It's a bit over the top to use a full fledged cryptography library when only one function is needed – Mark Bourke Dec 24 '16 at 16:14
  • Apologize for the old-thread comment... Perhaps, but common libraries are (presumably) always up to day with platform changes, thus yielding common results and minimizing fragmentation, and no one has to continually reinvent wheels or use a bunch of internet-found code that may or may not be reliable, fast, or patterned on standards. I'm all for minimizing dependencies, but in something like this, I look at OS options first, common language options second, and at third-party standard options next, and result to one-offs or "this guy's library is pretty good" options last. * shrug * – ChrisH Mar 25 '18 at 12:50
1

MD5 is a hashing algorithm, no need to use the bulky CommonCrypto library for this (and get rejected by Apple review), just use any md5 hashing library.

One such library I use is SwiftHash, a pure swift implementation of MD5 (based on http://pajhome.org.uk/crypt/md5/md5.html)

Nagendra Rao
  • 7,016
  • 5
  • 54
  • 92
1

Based on Cody's solution, I have an idea that we should clarify what's the result of MD5, because we may use the result as a hex string, or a Base64 string.

func md5(_ string: String) -> [UInt8] {
    let length = Int(CC_MD5_DIGEST_LENGTH)
    var digest = [UInt8](repeating: 0, count: length)

    if let d = string.data(using: String.Encoding.utf8) {
        _ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
            CC_MD5(body, CC_LONG(d.count), &digest)
        }
    }
    return digest
}

The function above actually returns a [UInt8], and based on this result, we can get any form of string, such as hex, base64.

If a hex string is wanted as the final result(as the question asks), we can keep using the rest part of Cody's solution

extension String {
    var md5Hex: String {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        return (0..<length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }
}

If a Base64 string is wanted as the final result

extension String {
    var md5Base64: String {
        let md5edData = Data(bytes: md5(self))
        return md5edData.base64EncodedString()
    }
}
Monsoir
  • 117
  • 1
  • 8
0

I found this library that seems to work pretty well.

https://github.com/onmyway133/SwiftHash

MD5("string")
Edmund Lee
  • 2,514
  • 20
  • 29
0

my two cents (if you need quickly md5 for Data/NSData, for example you downloaded or read binary for disk or netwkork)

(shameless from "Swift 5 answer as a String extension (based on the great answer of Invictus Cody")):

extension Data {
    var md5Value: String {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        var digest = [UInt8](repeating: 0, count: length)

        _ = self.withUnsafeBytes { body -> String in
            CC_MD5(body.baseAddress, CC_LONG(self.count), &digest)
            return ""
        }


        return (0 ..< length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }
} 

test:

print("test".data.md5Value) /*098f6bcd4621d373cade4e832627b4f6*/
ingconti
  • 10,876
  • 3
  • 61
  • 48