Reputation: 2408
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.
Upvotes: 122
Views: 100752
Reputation: 1335
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 ""
}
}
Upvotes: 111
Reputation: 169
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()
}
}
}
Upvotes: 16
Reputation: 767
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: "")
}
Upvotes: 4
Reputation: 2918
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()
}
}
Upvotes: 9
Reputation: 4092
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
Upvotes: 15
Reputation: 8885
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
...
Upvotes: 76
Reputation: 10474
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)
Upvotes: 3
Reputation: 540075
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)
}
Upvotes: 159
Reputation: 254
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
Upvotes: 3
Reputation: 5156
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.
Upvotes: 0
Reputation: 15857
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
}
}
Upvotes: 5
Reputation: 23398
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"
Upvotes: 18
Reputation: 9464
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()
}
}
Upvotes: 4