SHA256 in swift
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
Related videos on Youtube
Yury Alexandrov
Updated on January 20, 2021Comments
-
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
toCC_LONG
, for example.-
Benjamin Gruenbaum over 9 yearsYou can call ObjectiveC methods from swift directly, where exactly are you stuck?
-
Caleb over 9 yearsQuestions about translating from one language to another are off topic? Since when?
-
-
Adagio over 7 yearsWorks 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 over 6 yearsCan add this extension into Framework Project? How can create Objective-C Bridging Header into Framework Project ?
-
Zack Shapiro over 6 yearsThis looked great until I saw no iOS love.
-
Danila Kulakov about 6 yearsCan I use this function to NSData instance?
let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
-
Kevin Renskers over 4 yearsWon't the
import CryptoKit
break on iOS 12 though? It's an iOS 13.0+ only framework. -
touti over 4 yearsGood 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 over 4 years@KevinRenskers Use can use
#if canImport(CryptoKit)
for conditional import. Don't forget to set set-weak_framework CryptoKit
inOther Linker Flags
-
FedeH about 4 yearsNot 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 about 4 yearsIf 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 about 4 yearsAny differences?
var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
-
Vitalii almost 4 yearsThe 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 over 2 yearsThis 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 about 2 yearsExcellent, concise answer. Works in MacOS 11 CLI app.
-
Yadigar ZENGİN about 2 yearsThis one is a life sawer, literally.