SHA256 in schnell


82

Ich möchte sha256 in meinem Projekt verwenden, hatte jedoch einige Probleme beim Umschreiben von objC-Code in schnellen Code. Hilf mir bitte. Ich habe diese Antwort verwendet: Wie kann ich einen SHA-2-Hash (idealerweise SHA 256 oder SHA 512) in iOS berechnen?

Hier ist mein Code

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

es gibt mir Fehler alles da swift nicht umwandeln kann Intzu CC_LONG, zum Beispiel.


2
Sie können ObjectiveC-Methoden direkt von swift aus aufrufen. Wo genau stecken Sie fest?
Benjamin Gruenbaum

7
Fragen zum Übersetzen von einer Sprache in eine andere sind nicht zum Thema? Seit wann?
Caleb

@BenjaminGruenbaum Problem ist in der Zeichenfolge "unsigned char hash [CC_SHA1_DIGEST_LENGTH];"
Yury Alexandrov

@ ЮрикАлександров CUnsignedChar[]?
Benjamin Gruenbaum

Das andere Problem ist, dass Int nicht in CC_LONG konvertierbar ist
Yury Alexandrov

Antworten:


127

Sie müssen explizit zwischen Intund konvertieren CC_LONG, da Swift keine impliziten Konvertierungen wie in (Objective-) C durchführt.

Sie müssen auch hashein Array mit der erforderlichen Größe definieren.

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
}

Alternativ können Sie NSMutableDataden erforderlichen Puffer zuweisen:

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 für Swift 3 und 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 für 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)
}

5
Wie kann ich diese nsdata in einen String konvertieren, wie er ist, wenn ich versuche, ihn zu konvertieren? Gib mir einen falschen Wert
Kamal Upasena

Gute Antwort! Nur die FYI-API ist jetzt repeatElement ... anstatt zu wiederholen ... auf Int ab Xcode 8.2.1 für diejenigen, die in jüngerer Zeit darauf stoßen.
iOS Gamer

@ iOSGamer: Ich habe noch einmal überprüft, ob die Swift 3-Version oben korrekt ist und in Xcode 8.2.1 kompiliert wird :)
Martin R

4
Als Ergänzung zu dieser Lösung zu machen CC_SHA256_DIGEST_LENGTH, CC_SHA256und CC_LONGArbeit in Swift, müssen Sie hinzufügen , #import <CommonCrypto/CommonDigest.h>zu der Überbrückungs Header - Datei.
Abion47

3
Ihr Swift 5-Beispiel ist veraltet.
Claus Jørgensen

77

Die beste Antwort hat bei mir nicht funktioniert. Ich habe etwas im Web gefunden und ein wenig geändert und jetzt funktioniert es: D. Es ist für Swift 3 und 4.

Fügen Sie diese Erweiterung irgendwo in Ihr Projekt ein und verwenden Sie sie für eine Zeichenfolge wie die folgende: mystring.sha256 ()

extension String {

    func sha256() -> String {
        if let stringData = self.data(using: String.Encoding.utf8) {
            return hexStringFromData(input: digest(input: stringData as NSData))
        }
        return ""
    }

    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
    }

}

Übrigens benötigen Sie einen Bridging-Header, der CommonCrypto importiert. Wenn Sie noch keine haben, gehen Sie folgendermaßen vor:

  1. Neue Datei erstellen -> Header-Datei -> Speichern unter BridgingHeader
  2. In Build-Einstellungen -> Objective-C-Bridging-Header -> Hinzufügen ProjectName/BridgingHeader.h
  3. Fügen Sie #import <CommonCrypto/CommonHMAC.h>Ihre Header-Datei ein

1
Funktioniert wie ein Zauber @Andi. Nur eine Korrektur, die Xcode will: Diese Zeile: return hexStringFromData(input: digest(input: stringData)) Änderung durch: return hexStringFromData(input: digest(input: stringData as NSData))
Adagio

Kann diese Erweiterung in Framework Project hinzugefügt werden? Wie kann ein Objective-C-Bridging-Header in ein Framework-Projekt erstellt werden?
ChandreshKanetiya

Kann ich diese Funktion für die NSData-Instanz verwenden? let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
Danila Kulakov

21

Mit CryptoKitiOS13 haben wir jetzt die 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
}

Da utils für Protokoll definiert sind Digest, können Sie es für alle Art in verdauen CryptoKit, wie SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...


Gute Antwort, aber dafür muss die Zielversion mni 10 iOS13 sein. Ich musste sowohl diese Lösung als auch manuelles Rechnen je nach iOS-Version verwenden.
Touti

Irgendwelche Unterschiede? var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
Muhasturk

Die Lösung funktioniert, aber aufgrund dieses Problems in Xcode: openradar.appspot.com/7495817
Vitalii

17

Funktionen, die den SHA von NSData& String(Swift 3) geben:

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
}

Nehmen Sie in Ihren Bridging-Header auf:

#import "CommonCrypto/CommonCrypto.h"

Ich habe diesen Fehler für diesen Teil erhalten [let data = str.data (using: String.Encoding.utf8)] -> Fehler: Der Wert vom Typ 'Data' kann nicht in den erwarteten Argumenttyp 'String' konvertiert werden. Ich weiß bitte, was ich falsch mache
Kushal Shrestha

Haben Sie den Bridging-Header hinzugefügt? Dieser Code wird für mich unverändert von Swift 3-ish bis 4.1 erstellt. (Xcode 9.3 baut für mich).
Graham Perks

1
Dies ergibt keinen korrekten Hash. Fragen Sie bei einem Online-SHA-Generator nach.
Frédéric Adda

Vielleicht führen Ihre Online-Generatoren die Operation einschließlich einer abschließenden Null aus? Prüfen Sie einen Online-SHA256 oder vielleicht SHA-1 oder SHA-2?
Graham Perks

12

Eine Version für Swift 5, die CryptoKit unter iOS 13 verwendet und ansonsten auf CommonCrypto zurückgreift:

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

}

Verwendung:

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

Auch über den Swift-Paketmanager verfügbar:
https://github.com/ralfebert/TinyHashes


1
Wird die import CryptoKitPause nicht auf iOS 12 sein? Es ist ein Framework nur für iOS 13.0+.
Kevin Renskers

1
@ KevinRenskers Verwendung kann #if canImport(CryptoKit)für den bedingten Import verwendet werden. Vergessen Sie nicht, -weak_framework CryptoKitinOther Linker Flags
touti

Ich habe unter iOS12 und darunter nicht für mich gearbeitet und bin dem obigen Vorschlag gefolgt. Beim Starten der App wird jedoch weiterhin "Bibliothek nicht geladen: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" angezeigt.
Fede Henze

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

Wenn Sie Abwärtskompatibilität benötigen, funktioniert dies. Wenn Sie CryptoKit importieren, wie es die anderen Lösungen vorschlagen, stürzt die App unter iOS12 und darunter mit dem Fehler "Bibliothek nicht geladen: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" ab, wenn die App gestartet wird.
Fede Henze

5

Hier ist meine einfache 3-zeilige Swift 4-Funktion, die die Security Transforms-API verwendet, die Teil von Foundation unter macOS ist. (Leider können iOS-Programmierer diese Technik nicht verwenden.)

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

8
Das sah toll aus, bis ich keine iOS-Liebe sah.
Zack Shapiro

4

Hier ist eine Methode, die die CoreFoundation Security Transforms-API verwendet, sodass Sie nicht einmal eine Verknüpfung zu CommonCrypto herstellen müssen. Aus irgendeinem Grund ist die Verknüpfung mit CommmonCrypto mit Swift in 10.10 / Xcode 7 ein Drama, daher habe ich dies stattdessen verwendet.

Diese Methode liest aus einer NSInputStream, die Sie entweder aus einer Datei abrufen können, oder Sie können eine erstellen, die eine liest NSData, oder Sie können gebundene Lese- / Schreib-Streams für einen gepufferten Prozess erstellen.

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

Soweit ich weiß, ist dies nur unter OSX verfügbar, nicht unter iOS.
Zaph

3

Für 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

1

In Swift5 getestet.

Wenn Sie den Hash in String erhalten möchten ,

So habe ich es gemacht.

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

0

Ich bevorzuge:

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

Der gehetzte String ist base64-codiert.


0

Die anderen Antworten haben Leistungsprobleme bei der Berechnung von Digests aus großen Datenmengen (z. B. großen Dateien). Sie möchten nicht alle Daten gleichzeitig in den Speicher laden. Betrachten Sie den folgenden Ansatz mit 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)
        })
    }
}

Sie können es wie folgt verwenden:

let digest = SHA256Digest()
try digest.update(url: fileURL)
let result = digest.finalize().hexString
print(result)


0

Ich habe viele Antworten recherchiert und zusammengefasst:

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()
        }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.