50

in Objective-C, we can hash a string like this:

const char *cStr = [someString UTF8String];
unsigned char result[16];
CC_MD5( cStr, strlen(cStr), result );
md5String = [NSString stringWithFormat:
        @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
        result[0], result[1], result[2], result[3],
        result[4], result[5], result[6], result[7],
        result[8], result[9], result[10], result[11],
        result[12], result[13], result[14], result[15]
        ];

But CC_MD5 doesn't work in Swift. How do we deal with this?

mokagio
  • 16,391
  • 3
  • 51
  • 58
Suen
  • 1,110
  • 1
  • 7
  • 13
  • 1
    I have answered a similar question on how to [wrap CommonCrypto in Obj-C and use the wrapper in Swift](http://stackoverflow.com/questions/24099520/commonhmac-in-swift/24100156#24100156). See if that helps. – Erik Jun 09 '14 at 15:33
  • 2
    Thought I'd just point out that you don't "encrypt" a string with MD5, you hash it. – Chris Harrison Dec 29 '14 at 02:44

10 Answers10

87

This is what I came up with. It's an extension to String. Don't forget to add #import <CommonCrypto/CommonCrypto.h> to the ObjC-Swift bridging header that Xcode creates.

extension String  {
    var md5: String! {
        let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
        let strLen = CC_LONG(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)

        CC_MD5(str!, strLen, result)

        let hash = NSMutableString()
        for i in 0..<digestLen {
            hash.appendFormat("%02x", result[i])
        }

        result.dealloc(digestLen)

        return String(format: hash as String)
    }
 }
Guilherme Torres Castro
  • 15,135
  • 7
  • 59
  • 96
Jernej Strasner
  • 4,590
  • 29
  • 22
  • 1
    I think converting to `NSData` is better than a C string, because C strings cannot contain 0 bytes – newacct Jun 27 '14 at 04:05
  • 4
    In a framework is no bridging header, how to handle this in such a case? Which library / framework your are adding to "Link Binary With Libraries"? – Stephan Jul 05 '14 at 20:30
  • 1
    @newacct is right, using NSData (and ARC!) is safer. See below. – jstn Aug 05 '14 at 10:16
  • @jstn I don't see how it is safer if you make sure you correctly alloc/destroy. I create a string out of it anyway and never pass that data pointer struct around. – Jernej Strasner Aug 05 '14 at 12:48
  • @JernejStrasner in this case it's not such a big deal, but it's probably better to let ARC handle memory whenever possible. Why call malloc and free if you don't have to? – jstn Aug 05 '14 at 15:25
  • @jstn For performance reasons. Creating a NSMutableData object is more expensive than just allocating a chunk of memory. – Jernej Strasner Aug 05 '14 at 16:15
  • @JernejStrasner couldn't tell you exactly why, but after benchmarking both my version with `NSMutableData` is actually about 15% faster, at least on my system. Here's what I did: https://gist.github.com/jstn/b339ba87a4537ba98d85 – jstn Aug 10 '14 at 13:25
  • @jstn You're totally right, same on my system. Now this is interesting. Maybe NSMutableData does some internal optimizations. I'll do a little bit of research because I'm really curious. – Jernej Strasner Aug 10 '14 at 20:53
  • "Swift bridging header that Xcode creates" How do I get Xcode to create one? – zaph Sep 05 '14 at 23:12
  • There's an error in Xcode6GM: 'UnsafePointer – imike Sep 10 '14 at 09:10
37

Here's my version in Swift 3.0, I believe it to be safer and faster than the other answers here.

A bridging header with #import <CommonCrypto/CommonCrypto.h> is required.

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])
    }
}
squarefrog
  • 4,750
  • 4
  • 35
  • 64
jstn
  • 2,326
  • 1
  • 17
  • 15
  • 1
    In Xcode6GM I have an error with your code: NSData? doesn't have a member named bytes – imike Sep 10 '14 at 09:13
  • It may be safer and faster but it doesn't work. See the comment above this one. – Chris Harrison Dec 29 '14 at 02:49
  • 2
    NSData actually got private methods named hexString in several private system frameworks. If you named your method as hexString, it will eventually bite you. Check this url: https://github.com/nst/iOS-Runtime-Headers/search?utf8=%E2%9C%93&q=hexString – hankbao Jul 24 '15 at 17:18
  • Thanks! That tip about the bridging header was useful! (I'm aware that in even newer Swift versions, it's possible to use `import CommonCrypto` in Swift - but I can't go that far) – ReinstateMonica3167040 Jan 31 '21 at 17:21
12

I did pure Swift implementation of MD5 as part of CryptoSwift project.

I could copy code here but it uses extensions that are part of this project so it may be useless for copy&paste usage. However you can take a look there and use it.

Marcin
  • 3,694
  • 5
  • 32
  • 52
6

So here is the Solution and I know It will save your time 100%

BridgingHeader - > Used to Expose Objective-c code to a Swift Project

CommonCrypto - > is the file needed to use md5 hash

Since Common Crypto is a Objective-c file, you need to use BridgingHeader to use method needed for hashing


(e.g)

extension String {
func md5() -> String! {
    let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
    let strLen = CUnsignedInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    let digestLen = Int(CC_MD5_DIGEST_LENGTH)
    let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
    CC_MD5(str!, strLen, result)
    var hash = NSMutableString()
    for i in 0..<digestLen {
        hash.appendFormat("%02x", result[i])
    }
    result.destroy()
    return String(format: hash as String)
}

}

How to add Common Crypto into a Swift Project??

This link will teach you how (STEP by STEP).

I recommend using Bridging Header

*************Updated Swift 3****************

extension String {
func toMD5()  -> String {

        if let messageData = self.data(using:String.Encoding.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.hexString()
        }

        return self
    }
}


extension Data {

    func hexString() -> String {
        let string = self.map{ String($0, radix:16) }.joined()
        return string
    }

}

How to use?

let stringConvertedToMD5 = "foo".toMD5()

Basil Mariano
  • 2,437
  • 1
  • 20
  • 13
2

evntually if you want calculate MD5 out of NSData, take a look at this:

func md5() -> NSData {
    var ctx = UnsafePointer<CC_MD5_CTX>.alloc(sizeof(CC_MD5_CTX))
    CC_MD5_Init(ctx);

    CC_MD5_Update(ctx, self.bytes, UInt32(self.length));
    let length = Int(CC_MD5_DIGEST_LENGTH) * sizeof(Byte)
    var output = UnsafePointer<Byte>.alloc(length)
    CC_MD5_Final(output, ctx);

    let outData = NSData(bytes: output, length: Int(CC_MD5_DIGEST_LENGTH))
    output.destroy()
    ctx.destroy()

    //withUnsafePointer
    return outData;
}

to get idea.

Marcin
  • 3,694
  • 5
  • 32
  • 52
2

Xcode 6 beta 5 now uses an UnsafeMutablePointer in place of an UnsafePointer. String conversion also requires the format: argument label.

extension String {
    func md5() -> String! {
        let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
        let strLen = CUnsignedInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        let digestLen = Int(CC_MD5_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
        CC_MD5(str!, strLen, result)
        var hash = NSMutableString()
        for i in 0..<digestLen {
            hash.appendFormat("%02x", result[i])
        }
        result.destroy()
        return String(format: hash)
    }
}
Dan Greenfield
  • 341
  • 2
  • 5
2

Need import #import <CommonCrypto/CommonCrypto.h> into Bridging Header

I am calculating MD5 hash, but using only the first 16 byte I am using

class func hash(data: NSData) -> String {

    let data2 = NSMutableData(length: Int(CC_MD5_DIGEST_LENGTH))!
    CC_MD5(data.bytes, CC_LONG(data.length), UnsafeMutablePointer<UInt8>(data2.mutableBytes))
    let data3 =  UnsafePointer<CUnsignedChar>(data2.bytes)

    var hash = ""
    for (var i = 0; i < 16; ++i) {

        hash +=  String(format: "%02X", data3[i])
    }

    return hash
}
János
  • 32,867
  • 38
  • 193
  • 353
2

For cases where a bridging header isn't an option (eg, in a shell script), you can use the command line tool /sbin/md5 via NSTask:

import Foundation

func md5hash(string: String) -> String
{
  let t = NSTask()
  t.launchPath = "/sbin/md5"
  t.arguments = ["-q", "-s", string]
  t.standardOutput = NSPipe()

  t.launch()

  let outData = t.standardOutput.fileHandleForReading.readDataToEndOfFile()
  var outBytes = [UInt8](count:outData.length, repeatedValue:0)
  outData.getBytes(&outBytes, length: outData.length)

  var outString = String(bytes: outBytes, encoding: NSASCIIStringEncoding)

  assert(outString != nil, "failed to md5 input string")

  return outString!.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())
}

Usage:

let md5 = md5hash("hello world")

// 5eb63bbbe01eeed093cb22bb8f5acdc3
Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
2

Here are some changes I had to make to this code to get it working in Swift 5:

func md5(inString: String) -> String! {
    let str = inString.cString(using: String.Encoding.utf8)
    let strLen = CUnsignedInt(inString.lengthOfBytes(using: String.Encoding.utf8))
    let digestLen = Int(CC_MD5_DIGEST_LENGTH)
    let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
    CC_MD5(str!, strLen, result)
    var hash = NSMutableString()
    for i in 0..<digestLen {
        hash.appendFormat("%02x", result[i])
    }
    result.deallocate()
    return String(format: hash as String)
}
mokagio
  • 16,391
  • 3
  • 51
  • 58
A B Vijay Kumar
  • 859
  • 9
  • 10
0

Updated for contemporary Swift syntax (Swift 5.6 at the time of writing):

  • StringProtocol extension (so can be used on strings or substrings).
  • Allow caller to specify encoding, but default to UTF8.
  • Move actual MD5/SHA256 calculation into Data extension.
  • Use UnsafeRawBufferPointer rendition (rather than deprecated UnsafePointer rendition) of withUnsafeBytes and withMutableUnsafeBytes.
  • Add @availability warning for md5 that Apple has added to CC_MD5, to make sure that developers understand that this is not cryptographically sound.

Thus:

extension StringProtocol {
    @available(iOS, deprecated: 13.0, message: "This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).")
    func md5String(encoding: String.Encoding = .utf8) -> String? {
        data(using: encoding)?.md5.hexadecimal
    }

    @available(iOS, deprecated: 13.0, message: "This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).")
    func md5(encoding: String.Encoding = .utf8) -> Data? {
        data(using: encoding)?.md5
    }

    func sha256String(encoding: String.Encoding = .utf8) -> String? {
        data(using: encoding)?.sha256.hexadecimal
    }

    func sha256(encoding: String.Encoding = .utf8) -> Data? {
        data(using: encoding)?.sha256
    }
}

extension Data {
    var hexadecimal: String {
        map { String(format: "%02x", $0) }
            .joined()
    }

    @available(iOS, deprecated: 13.0, message: "This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).")
    var md5: Data {
        withUnsafeBytes { dataBuffer in
            var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH))
            digest.withUnsafeMutableBytes { digestBuffer in
                _ = CC_MD5(dataBuffer.baseAddress, CC_LONG(dataBuffer.count), digestBuffer.baseAddress)
            }
            return digest
        }
    }

    var sha256: Data {
        withUnsafeBytes { dataBuffer in
            var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
            digest.withUnsafeMutableBytes { digestBuffer in
                _ = CC_SHA256(dataBuffer.baseAddress, CC_LONG(dataBuffer.count), digestBuffer.baseAddress)
            }
            return digest
        }
    }
}

Used as follows:

let string = "Hello, World"

guard let result1 = string.md5String() else { return }
// 82bb413746aee42f89dea2b59614f9ef

guard let result2 = string.sha256String() else { return }
// 03675ac53ff9cd1535ccc7dfcdfa2c458c5218371f418dc136f2d19ac1fbe8a5
Rob
  • 415,655
  • 72
  • 787
  • 1,044