119

I want to use sha256 in my project, but I had some troubles rewriting objC code to swift code. Help me please. I used this answer: How can I compute a SHA-2 (ideally SHA 256 or SHA 512) hash in iOS?

Here's my code

var hash : [CUnsignedChar]
CC_SHA256(data.bytes, data.length, hash)
var res : NSData = NSData.dataWithBytes(hash, length: CC_SHA256_DIGEST_LENGTH)

it gives me error everything because swift cannot convert Int to CC_LONG, for example.

Community
  • 1
  • 1
Yury Alexandrov
  • 2,368
  • 5
  • 17
  • 17

13 Answers13

156

You have to convert explicitly between Int and CC_LONG, because Swift does not do implicit conversions, as in (Objective-)C.

You also have to define hash as an array of the required size.

func sha256(data : NSData) -> NSData {
    var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
    CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
    let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
    return res
}

Alternatively, you can use NSMutableData to allocate the needed buffer:

func sha256(data : NSData) -> NSData {
    let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
    CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(res.mutableBytes))
    return res
}

Update for Swift 3 and 4:

func sha256(data : Data) -> Data {
    var hash = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))
    data.withUnsafeBytes {
        _ = CC_SHA256($0, CC_LONG(data.count), &hash)
    }
    return Data(bytes: hash)
}

Update for Swift 5:

func sha256(data : Data) -> Data {
    var hash = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))
    data.withUnsafeBytes {
        _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
    }
    return Data(hash)
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 6
    how can i convert this nsdata to string as it is when i am trying to convert its give me wrong value – Kamal Upasena Dec 28 '15 at 05:26
  • 1
    Great answer! Just FYI API is now repeatElement... instead of repeating... on Int as of Xcode 8.2.1 for those coming across this more recently. – iOS Gamer Mar 21 '17 at 19:59
  • @iOSGamer: I double-checked that the Swift 3 version above is correct and compiles in Xcode 8.2.1 :) – Martin R Mar 21 '17 at 20:10
  • @MartinR So I double-checked and indeed even though it didn't show up in autocompletion it does exist, compiles and works. Being a newbie, a defer to your version. Sorry for the noise, and thanks once again! – iOS Gamer Mar 21 '17 at 20:18
  • 7
    As an addition to this solution, to make `CC_SHA256_DIGEST_LENGTH`, `CC_SHA256`, and `CC_LONG`work in Swift, you have to add `#import ` to the bridging header file. – Abion47 Aug 02 '18 at 19:35
  • 3
    Your Swift 5 example is out of date. – Claus Jørgensen Apr 02 '19 at 08:50
  • @ClausJørgensen: What is the problem? It compiles and runs in my Xcode 10.2 with Swift 5. – Martin R Apr 02 '19 at 08:53
  • Ok, it works if you do exactly as you wrote it. Xcode seems to have some issues with the `$0.baseAddress` part at random. I think it depends on how it infers the other arguments. If it can't infer the type to be UInt8 for the pointer, you can't access the `.baseAddress` property. (If you say, used a `Data` pointer instead of `[UInt8]`. – Claus Jørgensen Apr 02 '19 at 09:12
  • @ClausJørgensen: Yes, for the other argument you would have to bind the raw data pointer to an UInt8 pointer. Passing the address of an array is easier. It has been suggested to improve the interoperability with C functions, compare https://forums.swift.org/t/withunsafebytes-data-api-confusion/22142/16. – Martin R Apr 02 '19 at 10:56
  • @ClausJørgensen I have tested on Xcode 11.3.1 and Martins solution worked fine. I confirm it wont work in Xcode 11. – Sazzad Hissain Khan Mar 02 '20 at 09:21
107

Updated for Swift 5.

Put this extension somewhere in your project and use it on a string like this: mystring.sha256(), or on data with data.sha256()

import Foundation
import CommonCrypto

extension Data{
    public func sha256() -> String{
        return hexStringFromData(input: digest(input: self as NSData))
    }
    
    private func digest(input : NSData) -> NSData {
        let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
        var hash = [UInt8](repeating: 0, count: digestLength)
        CC_SHA256(input.bytes, UInt32(input.length), &hash)
        return NSData(bytes: hash, length: digestLength)
    }
    
    private  func hexStringFromData(input: NSData) -> String {
        var bytes = [UInt8](repeating: 0, count: input.length)
        input.getBytes(&bytes, length: input.length)
        
        var hexString = ""
        for byte in bytes {
            hexString += String(format:"%02x", UInt8(byte))
        }
        
        return hexString
    }
}

public extension String {
    func sha256() -> String{
        if let stringData = self.data(using: String.Encoding.utf8) {
            return stringData.sha256()
        }
        return ""
    }
}
Andreas
  • 1,295
  • 1
  • 11
  • 13
  • 1
    Works like a charm @Andi. Only one correction that Xcode wants: This line: `return hexStringFromData(input: digest(input: stringData))` Change by: `return hexStringFromData(input: digest(input: stringData as NSData))` – Adagio Sep 22 '16 at 19:13
  • Can add this extension into Framework Project? How can create Objective-C Bridging Header into Framework Project ? – ChandreshKanetiya Oct 26 '17 at 13:13
  • Can I use this function to NSData instance? `let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256() ` – Danila Kulakov Feb 05 '18 at 13:20
  • This one is a life sawer, literally. – Yadigar ZENGİN Feb 15 '22 at 14:29
66

With CryptoKit added in iOS13, we now have native Swift API:

import Foundation
import CryptoKit

// CryptoKit.Digest utils
extension Digest {
    var bytes: [UInt8] { Array(makeIterator()) }
    var data: Data { Data(bytes) }

    var hexStr: String {
        bytes.map { String(format: "%02X", $0) }.joined()
    }
}

func example() {
    guard let data = "hello world".data(using: .utf8) else { return }
    let digest = SHA256.hash(data: data)
    print(digest.data) // 32 bytes
    print(digest.hexStr) // B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9
}

Because utils are defined for protocol Digest, you can use it for all digest type in CryptoKit, like SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...

duan
  • 8,515
  • 3
  • 48
  • 70
  • Good answer, but this needs the target version to be mni 10 iOS13. I had to use both this solution and manual computing depending on iOS version. – touti Dec 27 '19 at 13:31
  • Any differences? `var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }` – muhasturk Apr 25 '20 at 04:51
  • The solution does work, but it is impossible to compile in Release configuration with target lower than iOS 11 because of this issue in Xcode: http://openradar.appspot.com/7495817 – Vitalii Apr 27 '20 at 19:59
  • Excellent, concise answer. Works in MacOS 11 CLI app. – Justin Vallely Feb 12 '22 at 02:31
  • 1
    So, just to confirm I'm not crazy - Digest is happy to calculate the hex string into the debug description (with extraneous text preventing it from being immediately usable) - but to get the hex string alone, you have to extend the class manually? – BricoleurDev Feb 02 '23 at 22:36
18

Functions giving the SHA from NSData & String (Swift 3):

func sha256(_ data: Data) -> Data? {
    guard let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH)) else { return nil }
    CC_SHA256((data as NSData).bytes, CC_LONG(data.count), res.mutableBytes.assumingMemoryBound(to: UInt8.self))
    return res as Data
}

func sha256(_ str: String) -> String? {
    guard
        let data = str.data(using: String.Encoding.utf8),
        let shaData = sha256(data)
        else { return nil }
    let rc = shaData.base64EncodedString(options: [])
    return rc
}

Include in your bridging header:

#import "CommonCrypto/CommonCrypto.h"
Graham Perks
  • 23,007
  • 8
  • 61
  • 83
  • I got this error on this portion [let data = str.data(using: String.Encoding.utf8)] -> Error : Cannot convert value of type 'Data' to expected argument type 'String'. My I please know what I am doing wrong – Kushal Shrestha Apr 13 '18 at 06:38
  • Did you add to the bridging header? This code builds for me unchanged from Swift 3-ish to 4.1. (Xcode 9.3 builds for me). – Graham Perks Apr 13 '18 at 15:08
  • 1
    This does not give a correct Hash. Check with an online SHA generator to see for yourself. – Frederic Adda Dec 07 '18 at 14:29
  • Perhaps your online generators perform the operation including a terminating zero? Are you checking an online SHA256, or maybe SHA-1 or SHA-2? – Graham Perks Dec 07 '18 at 15:29
17

I researched many answers and I summarized it:

import CryptoKit
import CommonCrypto
extension String {
    func hash256() -> String {
        let inputData = Data(utf8)
        
        if #available(iOS 13.0, *) {
            let hashed = SHA256.hash(data: inputData)
            return hashed.compactMap { String(format: "%02x", $0) }.joined()
        } else {
            var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
            inputData.withUnsafeBytes { bytes in
                _ = CC_SHA256(bytes.baseAddress, UInt32(inputData.count), &digest)
            }
            return digest.makeIterator().compactMap { String(format: "%02x", $0) }.joined()
        }
    }
}
Nam Nguyễn
  • 171
  • 2
  • 3
15

A version for Swift 5 that uses CryptoKit on iOS 13 and falls back to CommonCrypto otherwise:

import CommonCrypto
import CryptoKit
import Foundation

private func hexString(_ iterator: Array<UInt8>.Iterator) -> String {
    return iterator.map { String(format: "%02x", $0) }.joined()
}

extension Data {

    public var sha256: String {
        if #available(iOS 13.0, *) {
            return hexString(SHA256.hash(data: self).makeIterator())
        } else {
            var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
            self.withUnsafeBytes { bytes in
                _ = CC_SHA256(bytes.baseAddress, CC_LONG(self.count), &digest)
            }
            return hexString(digest.makeIterator())
        }
    }

}

Usage:

let string = "The quick brown fox jumps over the lazy dog"
let hexDigest = string.data(using: .ascii)!.sha256
assert(hexDigest == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592")

Also available via Swift package manager:
https://github.com/ralfebert/TinyHashes

Ralf Ebert
  • 3,556
  • 3
  • 29
  • 43
  • 1
    Won't the `import CryptoKit` break on iOS 12 though? It's an iOS 13.0+ only framework. – Kevin Renskers Nov 27 '19 at 11:42
  • 2
    @KevinRenskers Use can use `#if canImport(CryptoKit)` for conditional import. Don't forget to set set `-weak_framework CryptoKit` in `Other Linker Flags` – touti Dec 27 '19 at 15:06
  • Not working for me on iOS12 and below, I followed the above suggestion but I'm still getting "Library not loaded: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" when the app starts. – FedeH Apr 02 '20 at 08:42
9
import CommonCrypto

public extension String {

  var sha256: String {
      let data = Data(utf8)
      var hash = [UInt8](repeating: 0,  count: Int(CC_SHA256_DIGEST_LENGTH))

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

      return hash.map { String(format: "%02hhx", $0) }.joined()
  }
}
zero3nna
  • 2,770
  • 30
  • 28
  • If you need to have backward compatibility this is will works. Importing CryptoKit as the other solutions suggest, will crash the app on iOS12 and below with this error "Library not loaded: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" when the app starts. – FedeH Apr 02 '20 at 08:53
5

Here's my simple 3-line Swift 4 function for this using the Security Transforms API, which is part of Foundation on macOS. (Unfortunately iOS programmers cannot use this technique.)

import Foundation

extension Data {
    public func sha256Hash() -> Data {
        let transform = SecDigestTransformCreate(kSecDigestSHA2, 256, nil)
        SecTransformSetAttribute(transform, kSecTransformInputAttributeName, self as CFTypeRef, nil)
        return SecTransformExecute(transform, nil) as! Data
    }
}
Nick Moore
  • 15,547
  • 6
  • 61
  • 83
4

Here's a method that uses the CoreFoundation Security Transforms API, so you don't even need to link to CommonCrypto. For some reason in 10.10/Xcode 7 linking to CommmonCrypto with Swift is drama so I used this instead.

This method reads from an NSInputStream, which you can either get from a file, or you can make one that reads an NSData, or you can make bound reader/writer streams for a buffered process.

// digestType is from SecDigestTransform and would be kSecDigestSHA2, etc 
func digestForStream(stream : NSInputStream,
    digestType type : CFStringRef, length : Int) throws -> NSData {

    let transform = SecTransformCreateGroupTransform().takeRetainedValue()

    let readXform = SecTransformCreateReadTransformWithReadStream(stream as CFReadStreamRef).takeRetainedValue()

    var error : Unmanaged<CFErrorRef>? = nil

    let digestXform : SecTransformRef = try {
        let d = SecDigestTransformCreate(type, length, &error)
        if d == nil {
            throw error!.takeUnretainedValue()
        } else {
            return d.takeRetainedValue()
        }
    }()

    SecTransformConnectTransforms(readXform, kSecTransformOutputAttributeName,
        digestXform, kSecTransformInputAttributeName,
        transform, &error)
    if let e = error { throw e.takeUnretainedValue() }

    if let output = SecTransformExecute(transform, &error) as? NSData {
        return output
    } else {
        throw error!.takeUnretainedValue()
    }
}
iluvcapra
  • 9,436
  • 2
  • 30
  • 32
4

Tested in Swift5.

In case you want to get the hash in String,

this is how I did.

private func getHash(_ phrase:String) -> String{
    let data = phrase.data(using: String.Encoding.utf8)!
    let length = Int(CC_SHA256_DIGEST_LENGTH)
    var digest = [UInt8](repeating: 0, count: length)
    data.withUnsafeBytes {
        _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &digest)
    }
    return digest.map { String(format: "%02x", $0) }.joined(separator: "")
}
3

For Swift 5:

guard let data = self.data(using: .utf8) else { return nil }
    var sha256 = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
    sha256.withUnsafeMutableBytes { sha256Buffer in
        data.withUnsafeBytes { buffer in
            let _ = CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), sha256Buffer.bindMemory(to: UInt8.self).baseAddress)
        }
    }

    return sha256
mohammad_Z74
  • 244
  • 2
  • 10
3

The other answers will have performance problems for calculating digests from large amounts of data (e.g. large files). You will not want to load all data into memory at once. Consider the following approach using update/finalize:

final class SHA256Digest {

    enum InputStreamError: Error {
        case createFailed(URL)
        case readFailed
    }

    private lazy var context: CC_SHA256_CTX = {
        var shaContext = CC_SHA256_CTX()
        CC_SHA256_Init(&shaContext)
        return shaContext
    }()
    private var result: Data? = nil

    init() {
    }

    func update(url: URL) throws {
        guard let inputStream = InputStream(url: url) else {
            throw InputStreamError.createFailed(url)
        }
        return try update(inputStream: inputStream)
    }

    func update(inputStream: InputStream) throws {
        guard result == nil else {
            return
        }
        inputStream.open()
        defer {
            inputStream.close()
        }
        let bufferSize = 4096
        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        defer {
            buffer.deallocate()
        }
        while true {
            let bytesRead = inputStream.read(buffer, maxLength: bufferSize)
            if bytesRead < 0 {
                //Stream error occured
                throw (inputStream.streamError ?? InputStreamError.readFailed)
            } else if bytesRead == 0 {
                //EOF
                break
            }
            self.update(bytes: buffer, length: bytesRead)
        }
    }

    func update(data: Data) {
        guard result == nil else {
            return
        }
        data.withUnsafeBytes {
            self.update(bytes: $0, length: data.count)
        }
    }

    func update(bytes: UnsafeRawPointer, length: Int) {
        guard result == nil else {
            return
        }
        _ = CC_SHA256_Update(&self.context, bytes, CC_LONG(length))
    }

    func finalize() -> Data {
        if let calculatedResult = result {
            return calculatedResult
        }
        var resultBuffer = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        CC_SHA256_Final(&resultBuffer, &self.context)
        let theResult = Data(bytes: resultBuffer)
        result = theResult
        return theResult
    }
}

extension Data {

    private static let hexCharacterLookupTable: [Character] = [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "a",
        "b",
        "c",
        "d",
        "e",
        "f"
    ]

    var hexString: String {
        return self.reduce(into: String(), { (result, byte) in
            let c1: Character = Data.hexCharacterLookupTable[Int(byte >> 4)]
            let c2: Character = Data.hexCharacterLookupTable[Int(byte & 0x0F)]
            result.append(c1)
            result.append(c2)
        })
    }
}

You could use it as follows:

let digest = SHA256Digest()
try digest.update(url: fileURL)
let result = digest.finalize().hexString
print(result)
Werner Altewischer
  • 10,080
  • 4
  • 53
  • 60
  • This is _exactly_ what I was looking for! It was sadly incompatible with Swift 5, but with a few simple fixes it worked: https://gist.github.com/yspreen/95627d72bedcd47f1c0d29271b5600ea – yspreen Jul 31 '21 at 14:33
0

I prefer to use:

extension String {
    var sha256:String? {
        guard let stringData = self.data(using: String.Encoding.utf8) else { return nil }
        return digest(input: stringData as NSData).base64EncodedString(options: [])
    }

    private func digest(input : NSData) -> NSData {
        let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
        var hash = [UInt8](repeating: 0, count: digestLength)
        CC_SHA256(input.bytes, UInt32(input.length), &hash)
        return NSData(bytes: hash, length: digestLength)
    }
}

The hasded String is base64 encoded.

DàChún
  • 4,751
  • 1
  • 36
  • 39