SHA256 hızlı bir şekilde


86

Projemde sha256 kullanmak istiyorum, ancak objC kodunu swift koduna yeniden yazarken bazı sorunlar yaşadım. Bana yardım et lütfen. Şu yanıtı kullandım: iOS'ta bir SHA-2 (ideal olarak SHA 256 veya SHA 512) karmasını nasıl hesaplayabilirim?

İşte kodum

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

hızlı dönüştürmek olamaz çünkü bana hata her şeyi verir Intiçin CC_LONGörneğin.


2
ObjectiveC yöntemlerini doğrudan swift'ten çağırabilirsiniz, tam olarak nerede sıkışmışsınız?
Benjamin Gruenbaum

7
Bir dilden diğerine çeviri ile ilgili sorular konu dışı mı? Ne zamandan beri?
Caleb

@BenjaminGruenbaum problemi "işaretsiz karakter hash [CC_SHA1_DIGEST_LENGTH];" dizesinde.
Yury Alexandrov

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

diğer sorun Int'in CC_LONG'a dönüştürülememesi
Yury Alexandrov

Yanıtlar:


133

Açıkça Intve arasında dönüştürme yapmanız gerekir CC_LONG, çünkü Swift (Amaç-) C'de olduğu gibi örtük dönüşümler yapmaz.

Ayrıca hashgerekli büyüklükte bir dizi olarak tanımlamalısınız .

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
}

Alternatif olarak, NSMutableDatagerekli tamponu tahsis etmek için kullanabilirsiniz :

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
}

Swift 3 ve 4 için güncelleme:

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

Swift 5 Güncellemesi:

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
bu
nsdata'yı

Mükemmel cevap! Sadece FYI API şimdi tekrarlamak yerine tekrar Elementtir ... Bu daha yakın zamanda karşılaşanlar için Xcode 8.2.1'den itibaren Int üzerinde tekrarlamak yerine.
iOS Oyuncu

@iOSGamer: Yukarıdaki Swift 3 sürümünün doğru olduğunu ve Xcode 8.2.1'de derlendiğini iki kez kontrol ettim :)
Martin R

4
Yapmak için bu çözüm için bir ek olarak CC_SHA256_DIGEST_LENGTH, CC_SHA256ve CC_LONGSwift çalışma, eklemek zorunda #import <CommonCrypto/CommonDigest.h>köprüleme başlık dosyasına.
Abion47

3
Swift 5 örneğiniz güncel değil.
Claus Jørgensen

79

En iyi cevap benim için işe yaramadı. Web'de bir şey buldum ve biraz değiştirdim ve şimdi çalışıyor: D. Swift 3 ve 4 için.

Bu uzantıyı projenizde bir yere koyun ve şöyle bir dizede kullanın: 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
    }

}

Bu arada, CommonCrypto'yu içe aktaran bir Bridging Header'a ihtiyacınız var. Eğer hesabınız yoksa şu adımları izleyin:

  1. Yeni Dosya Oluştur -> Başlık Dosyası -> Farklı Kaydet BridgingHeader
  2. Derleme Ayarlarında -> Objective-C Bridging Header -> ekle ProjectName/BridgingHeader.h
  3. #import <CommonCrypto/CommonHMAC.h>Başlık Dosyanızı koyun

1
Bir cazibe gibi çalışıyor @Andi. Xcode'un istediği yalnızca bir düzeltme: Bu satır: return hexStringFromData(input: digest(input: stringData)) Değiştir: return hexStringFromData(input: digest(input: stringData as NSData))
Adagio

Bu uzantıyı Framework Project'e ekleyebilir misiniz? Objective-C Bridging Header'ı Framework Project'e nasıl oluşturabilirim?
ChandreshKanetiya

Bu işlevi NSData örneğinde kullanabilir miyim? let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
Danila Kulakov

25

CryptoKitİOS13'te eklenenler ile artık yerel Swift API'miz var:

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
}

Utils protokol için tanımlanan Çünkü Digesttüm türünü sindirmek için, kullanmadan CryptoKitgibi SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...


Güzel cevap, ancak bunun için hedef sürümün mni 10 iOS13 olması gerekiyor. İOS sürümüne bağlı olarak hem bu çözümü hem de manuel hesaplamayı kullanmak zorunda kaldım.
touti

Herhangi bir farklılık var mı? var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
muhasturk

Çözüm işe yarıyor, ancak Xcode'daki
Vitalii

17

SHA'yı NSData& String(Swift 3) ' ten veren işlevler :

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
}

Köprü başlığınıza şunları ekleyin:

#import "CommonCrypto/CommonCrypto.h"

Bu hatayı bu kısımda aldım [let data = str.data (using: String.Encoding.utf8)] -> Hata: 'Data' türünün değeri beklenen 'String' bağımsız değişken türüne dönüştürülemiyor. Benim ben yanlış yapıyorum biliyor lütfen
Kushal Shrestha

Köprüleme başlığına eklediniz mi? Bu kod benim için Swift 3-ish'den 4.1'e değişmeden oluşturuldu. (Xcode 9.3 benim için oluşturulmuştur).
Graham Perks

1
Bu doğru bir Hash vermez. Kendiniz görmek için çevrimiçi bir SHA oluşturucu ile kontrol edin.
Frédéric Adda

Belki de çevrim içi üreticileriniz sonlandırma sıfırı içeren işlemi gerçekleştirir? Çevrimiçi bir SHA256 veya belki SHA-1 veya SHA-2'yi mi kontrol ediyorsunuz?
Graham Perks

12

İOS 13'te CryptoKit kullanan ve aksi takdirde CommonCrypto'ya geri dönen Swift 5 sürümü:

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

}

Kullanım:

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

Swift paket yöneticisi aracılığıyla da edinilebilir:
https://github.com/ralfebert/TinyHashes


1
import CryptoKitYine de iOS 12'deki kırılma olmayacak mı? Yalnızca iOS 13.0+ çerçevesidir.
Kevin Renskers

1
@KevinRenskers Kullanım #if canImport(CryptoKit)koşullu içe aktarma için kullanılabilir . Set seti unutma -weak_framework CryptoKitiçindeOther Linker Flags
touti

Benim için iOS12 ve altında çalışmıyor, yukarıdaki öneriyi izledim ancak uygulama başladığında hala "Kitaplık yüklenmedi: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" mesajı alıyorum.
Fede Henze

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

Geriye dönük uyumluluğa ihtiyacınız varsa, bu işe yarayacaktır. CryptoKit'i diğer çözümlerin önerdiği gibi içe aktarmak, uygulama başladığında "Kitaplık yüklenmedi: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" hatasını vererek iOS12 ve altındaki uygulamaların çökmesine neden olur.
Fede Henze

5

İşte macOS'ta Foundation'ın bir parçası olan Security Transforms API'yi kullanarak bunun için basit 3 satırlı Swift 4 işlevim. (Maalesef iOS programcıları bu tekniği kullanamaz.)

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
Bu, iOS aşkı görmeyene kadar harika görünüyordu.
Zack Shapiro

4

İşte CoreFoundation Security Transforms API'yi kullanan bir yöntem, böylece CommonCrypto'ya bağlanmanıza bile gerek kalmaz. Bazı nedenlerden dolayı 10.10 / Xcode 7'de Swift ile CommmonCrypto'ya bağlanmak drama, onun yerine bunu kullandım.

Bu yöntem NSInputStreambir dosyadan alabileceğiniz bir dosyadan okur NSDataveya bir okuyan bir tane yapabilir veya arabelleğe alınmış bir işlem için bağlı okuyucu / yazıcı akışları oluşturabilirsiniz.

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

Anladığım kadarıyla bu sadece OSX'te mevcut, iOS'ta değil.
zaph

3

Swift 5 için:

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

3

Swift5'te test edildi.

String'de hash almak istemeniz durumunda ,

ben böyle yaptım.

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

2

Birçok cevabı araştırdım ve özetledim:

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

0

Kullanmayı tercih ederim:

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

Hasded String, base64 olarak kodlanmıştır.


0

Diğer cevaplar, büyük miktarda veriden (örneğin, büyük dosyalar) özetleri hesaplamak için performans problemlerine sahip olacaktır. Tüm verileri hafızaya aynı anda yüklemek istemeyeceksiniz. Update / finalize kullanarak aşağıdaki yaklaşımı düşünün:

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

Aşağıdaki gibi kullanabilirsiniz:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.