SHA256 in swift

75,494

Solution 1

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

Solution 2

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 ""
    }
}

Solution 3

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

Solution 4

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"

Solution 5

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

Share:
75,494

Related videos on Youtube

Yury Alexandrov
Author by

Yury Alexandrov

Updated on January 20, 2021

Comments

  • Yury Alexandrov
    Yury Alexandrov over 3 years

    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.

    • Benjamin Gruenbaum
      Benjamin Gruenbaum over 9 years
      You can call ObjectiveC methods from swift directly, where exactly are you stuck?
    • Caleb
      Caleb over 9 years
      Questions about translating from one language to another are off topic? Since when?
  • Adagio
    Adagio over 7 years
    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))
  • ChandreshKanetiya
    ChandreshKanetiya over 6 years
    Can add this extension into Framework Project? How can create Objective-C Bridging Header into Framework Project ?
  • Zack Shapiro
    Zack Shapiro over 6 years
    This looked great until I saw no iOS love.
  • Danila Kulakov
    Danila Kulakov about 6 years
    Can I use this function to NSData instance? let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
  • Kevin Renskers
    Kevin Renskers over 4 years
    Won't the import CryptoKit break on iOS 12 though? It's an iOS 13.0+ only framework.
  • touti
    touti over 4 years
    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
    touti over 4 years
    @KevinRenskers Use can use #if canImport(CryptoKit) for conditional import. Don't forget to set set -weak_framework CryptoKit in Other Linker Flags
  • FedeH
    FedeH about 4 years
    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
    FedeH about 4 years
    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.
  • muhasturk
    muhasturk about 4 years
    Any differences? var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
  • Vitalii
    Vitalii almost 4 years
    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: openradar.appspot.com/7495817
  • yspreen
    yspreen over 2 years
    This is exactly what I was looking for! It was sadly incompatible with Swift 5, but with a few simple fixes it worked: gist.github.com/yspreen/95627d72bedcd47f1c0d29271b5600ea
  • Justin Vallely
    Justin Vallely about 2 years
    Excellent, concise answer. Works in MacOS 11 CLI app.
  • Yadigar ZENGİN
    Yadigar ZENGİN about 2 years
    This one is a life sawer, literally.

Related