स्विफ्ट में SHA256


84

मैं अपने प्रोजेक्ट में sha256 का उपयोग करना चाहता हूं, लेकिन मुझे स्विफ्ट कोड पर objC कोड को फिर से लिखने में कुछ परेशानी थी। कृपया मेरी मदद करें। मैंने इस उत्तर का उपयोग किया: मैं iOS में SHA-2 (आदर्श रूप से SHA 256 या SHA 512) हैश की गणना कैसे कर सकता हूं?

यहाँ मेरा कोड है

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

यह मुझे त्रुटि सब कुछ देता है, क्योंकि तेजी से परिवर्तित नहीं कर सकते Intकरने के लिए CC_LONG, उदाहरण के लिए।


2
आप सीधे स्विफ्ट से ObjectiveC तरीकों को कॉल कर सकते हैं, जहां आप फंस गए हैं?
बेंजामिन ग्रुएनबाम

7
एक भाषा से दूसरी भाषा में अनुवाद करने के बारे में सवाल करना विषय है? कब से?
कालेब

@BenjaminGruenbaum समस्या स्ट्रिंग में है "अहस्ताक्षरित चार हैश [CC_SHA1_DIGEST_LENGTH];"
यूरी अलेक्जेंड्रोव

@ CUnsignedChar[]? РикАлександров ?
बेंजामिन ग्रुएनबाम

दूसरी समस्या यह है कि Int cc_Long के लिए परिवर्तनीय नहीं है
यूरी अलेक्जेंड्रोव

जवाबों:


132

आपको स्पष्ट रूप से Intऔर के बीच में कनवर्ट करना होगा CC_LONG, क्योंकि स्विफ्ट अंतर्निहित रूपांतरण नहीं करता है, जैसा कि (उद्देश्य-) सी।

आपको hashआवश्यक आकार की एक सरणी के रूप में भी परिभाषित करना होगा।

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
}

वैकल्पिक रूप से, आप NSMutableDataआवश्यक बफर आवंटित करने के लिए उपयोग कर सकते हैं :

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
}

स्विफ्ट 3 और 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)
}

स्विफ्ट 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
मैं इस nsdata को स्ट्रिंग में कैसे बदल सकता हूं क्योंकि जब मैं इसका गलत मूल्य देने की कोशिश कर रहा हूं
कमल उपसेना

बहुत बढ़िया जवाब! बस FYI API अब दोहरा रहा है ... दोहराने के बजाय ... Int पर Xcode 8.2.1 के रूप में जो हाल ही में इस पार आने वालों के लिए है।
iOS गेमर

@iOSGamer: मैंने डबल-चेक किया कि ऊपर का 3 संस्करण स्विफ्ट सही है और Xcode 8.2.1 में संकलित किया गया है :)
मार्टिन आर

4
इस समाधान के अतिरिक्त CC_SHA256_DIGEST_LENGTH, स्विफ्ट बनाने , CC_SHA256और CC_LONGकाम करने के लिए, आपको #import <CommonCrypto/CommonDigest.h>ब्रिजिंग हेडर फ़ाइल में जोड़ना होगा।
Abion47

3
आपका स्विफ्ट 5 उदाहरण पुराना है।
क्लॉस जोर्जेंसन

79

शीर्ष जवाब मेरे लिए काम नहीं किया। मैंने वेब में कुछ पाया और इसे थोड़ा बदल दिया और अब यह काम करता है: डी। यह स्विफ्ट 3 और 4 के लिए है।

इस विस्तार को अपनी परियोजना में कहीं रखें और इसे इस तरह से स्ट्रिंग पर उपयोग करें: mysteryring.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
    }

}

वैसे आपको कॉमन क्रिप्टो आयात करने वाले ब्रिजिंग हेडर की आवश्यकता है। यदि आपके पास इन चरणों का पालन नहीं है:

  1. नई फ़ाइल बनाएँ -> शीर्ष लेख फ़ाइल -> इस रूप में सहेजें BridgingHeader
  2. बिल्ड सेटिंग्स में -> उद्देश्य-सी ब्रिजिंग हेडर -> जोड़ें ProjectName/BridgingHeader.h
  3. #import <CommonCrypto/CommonHMAC.h>अपनी हैडर फ़ाइल में रखें

1
एक आकर्षण @Andi की तरह काम करता है। केवल एक सुधार जो Xcode चाहता है: यह पंक्ति: इसके return hexStringFromData(input: digest(input: stringData)) द्वारा बदलें: return hexStringFromData(input: digest(input: stringData as NSData))
Adagio

क्या इस विस्तार को फ्रेमवर्क प्रोजेक्ट में जोड़ा जा सकता है? कैसे फ्रेमवर्क प्रोजेक्ट में ऑब्जेक्टिव-सी ब्रिजिंग हैडर बना सकते हैं?
चंद्रेशकेतनिया

क्या मैं NSData उदाहरण के लिए इस फ़ंक्शन का उपयोग कर सकता हूं? let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
दानिला कुलकोव

24

CryptoKitIOS13 में जोड़े जाने के साथ , अब हमारे पास मूल स्विफ्ट एपीआई है:

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 प्रोटोकॉल के लिए परिभाषित कर रहे हैं Digest, आप इसे उपयोग कर सकते हैं सभी में पचाने प्रकार के लिए CryptoKit, की तरह SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...


अच्छा जवाब है, लेकिन इसके लिए लक्ष्य संस्करण की आवश्यकता है mni 10 iOS13। मुझे आईओएस संस्करण के आधार पर इस समाधान और मैनुअल कंप्यूटिंग दोनों का उपयोग करना था।
Touti

कोई मतभेद? var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
मुहूर्त

समाधान काम करता है, लेकिन यह Xcode में इस मुद्दे के कारण iOS 11 से कम लक्ष्य के साथ रिलीज़ कॉन्फ़िगरेशन में संकलित करना असंभव है: Openradar.appspot.com/7495817
Vitalii

17

NSDataऔर String(स्विफ्ट 3) से SHA देने वाले कार्य :

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
}

अपने ब्रिजिंग हेडर में शामिल करें:

#import "CommonCrypto/CommonCrypto.h"

मुझे इस भाग पर यह त्रुटि मिली [डेटा = str.data (का उपयोग करके: String.Encoding.utf8)] -> त्रुटि: अपेक्षित तर्क प्रकार 'स्ट्रिंग' के प्रकार 'डेटा' के मूल्य को परिवर्तित नहीं कर सकता। मेरी कृपया मुझे पता है कि मैं क्या गलत कर रहा हूँ
कुशाल श्रेष्ठ

क्या आपने ब्रिजिंग हेडर में जोड़ा है? यह कोड मेरे लिए स्विफ्ट 3-ईश से 4.1 तक अपरिवर्तित है। (Xcode 9.3 मेरे लिए बनाता है)।
ग्राहम पर्क्स

1
यह सही हैश नहीं देता है। अपने लिए देखने के लिए एक ऑनलाइन SHA जनरेटर की जाँच करें।
Frédéric Adda

शायद आपके ऑनलाइन जनरेटर एक समाप्ति शून्य सहित ऑपरेशन करते हैं? क्या आप एक ऑनलाइन SHA256, या शायद SHA-1 या SHA-2 की जांच कर रहे हैं?
ग्राहम पर्क्स

12

स्विफ्ट 5 का एक संस्करण जो iOS 13 पर CryptoKit का उपयोग करता है और अन्यथा CommonCrypto पर वापस आता है:

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

}

उपयोग:

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

स्विफ्ट पैकेज मैनेजर के माध्यम से भी उपलब्ध है:
https://github.com/ralfebert/TinyHashes


1
import CryptoKitहालांकि iOS 12 पर ब्रेक नहीं होगा ? यह एक iOS 13.0+ केवल ढांचा है।
केविन रैंकर्स

1
@KevinRenskers #if canImport(CryptoKit)सशर्त आयात के लिए उपयोग कर सकते हैं । सेट सेट के लिए मत भूलना -weak_framework CryptoKitमेंOther Linker Flags
Touti

मेरे लिए iOS12 और उसके नीचे काम नहीं करने पर, मैंने उपरोक्त सुझाव का पालन किया लेकिन मैं अभी भी "लाइब्रेरी लोडेड नहीं: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" एप शुरू होने पर ले रहा हूं।
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()
  }
}

यदि आपको पिछड़े अनुकूलता की आवश्यकता है तो यह काम करेगा। अन्य समाधानों के अनुसार CryptoKit को आयात करना, iOS 12 पर ऐप को क्रैश कर देगा और नीचे इस त्रुटि के साथ "लाइब्रेरी लोड नहीं: /System/Library/Frameworks/CryptoKit.framework/CryptoKit" जब ऐप शुरू होता है।
Fede Henze

5

सुरक्षा ट्रांसफ़ॉर्म एपीआई का उपयोग करके इसके लिए मेरा सरल 3-लाइन स्विफ्ट 4 फ़ंक्शन है, जो मैकओएस पर फाउंडेशन का हिस्सा है। (दुर्भाग्य से iOS प्रोग्रामर इस तकनीक का उपयोग नहीं कर सकते हैं।)

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
यह बहुत अच्छा लग रहा था जब तक कि मैंने कोई आईओएस प्यार नहीं देखा।
ज़ैक शापिरो

4

यहां एक विधि है जो CoreFoundation Security Transforms API का उपयोग करती है, इसलिए आपको CommonCrypto से लिंक करने की आवश्यकता नहीं है। 10.10 / Xcode 7 में किसी कारण से Swift के साथ CommmonCrypto को जोड़ना नाटक है, इसलिए मैंने इसके बजाय इसका उपयोग किया।

यह विधि एक लेखक से पढ़ती है NSInputStream, जिसे आप या तो एक फ़ाइल से प्राप्त कर सकते हैं, या आप एक पढ़ सकते हैं जो एक पढ़ता है NSData, या आप एक बफ़र प्रक्रिया के लिए बाध्य रीडर / लेखक स्ट्रीम बना सकते हैं।

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

जो मैं समझता हूं कि यह केवल OSX पर उपलब्ध है, iOS पर नहीं।
zaph

3

स्विफ्ट 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

3

Swift5 में परीक्षण किया गया।

यदि आप स्ट्रिंग में हैश प्राप्त करना चाहते हैं ,

मैंने ऐसा ही किया।

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

मैंने कई उत्तरों पर शोध किया और मैंने इसे संक्षेप में प्रस्तुत किया:

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

मैं उपयोग करना पसंद करता हूं:

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

हैडिंग स्ट्रिंग बेस 64 एनकोडेड है।


0

अन्य उत्तरों में बड़ी मात्रा में डेटा (जैसे बड़ी फाइलें) से डिगस्ट्स की गणना के लिए प्रदर्शन समस्याएं होंगी। आप एक बार में सभी डेटा को मेमोरी में लोड नहीं करना चाहेंगे। अद्यतन का उपयोग करके निम्नलिखित दृष्टिकोण पर विचार करें / अंतिम रूप दें:

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

आप इसका उपयोग इस प्रकार कर सकते हैं:

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.