Chcę użyć sha256 w moim projekcie, ale miałem pewne problemy z przepisaniem kodu objC na szybki kod. Pomóż mi proszę. Użyłem tej odpowiedzi: Jak mogę obliczyć skrót SHA-2 (najlepiej SHA 256 lub SHA 512) w iOS?
Oto mój kod
var hash : [CUnsignedChar]
CC_SHA256(data.bytes, data.length, hash)
var res : NSData = NSData.dataWithBytes(hash, length: CC_SHA256_DIGEST_LENGTH)
to wszystko daje mi o błędzie, ponieważ Swift nie może konwertować Int
do CC_LONG
, na przykład.
CUnsignedChar[]
?Odpowiedzi:
Musisz konwertować jawnie między
Int
iCC_LONG
, ponieważ Swift nie wykonuje niejawnych konwersji, jak w (Objective-) C.Musisz także zdefiniować
hash
jako tablicę o wymaganym rozmiarze.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 }
Alternatywnie możesz użyć
NSMutableData
do przydzielenia potrzebnego bufora: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 }
Aktualizacja dla Swift 3 i 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) }
Aktualizacja dla 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) }
źródło
CC_SHA256_DIGEST_LENGTH
,CC_SHA256
iCC_LONG
pracy w Swift, trzeba dodać#import <CommonCrypto/CommonDigest.h>
do pliku nagłówka pomostowego.Najlepsza odpowiedź nie zadziałała dla mnie. Znalazłem coś w sieci i trochę to zmieniłem i teraz działa: D. Dotyczy wersji Swift 3 i 4.
Umieść to rozszerzenie gdzieś w swoim projekcie i użyj go na łańcuchu takim jak: 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 } }
Przy okazji potrzebujesz nagłówka Bridging, który importuje CommonCrypto. Jeśli go nie masz, wykonaj następujące czynności:
BridgingHeader
ProjectName/BridgingHeader.h
#import <CommonCrypto/CommonHMAC.h>
swój plik nagłówkowyźródło
return hexStringFromData(input: digest(input: stringData))
Zmień przez:return hexStringFromData(input: digest(input: stringData as NSData))
let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
Po
CryptoKit
dodaniu w iOS13 mamy teraz natywny interfejs 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 }
Ponieważ utils są zdefiniowane dla protokołu
Digest
, można go używać dla wszystkich strawienia wpisaćCryptoKit
, jakSHA384Digest
,SHA512Digest
,SHA1Digest
,MD5Digest
...źródło
var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
Funkcje dające SHA z
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 }
Uwzględnij w swoim nagłówku mostkowania:
#import "CommonCrypto/CommonCrypto.h"
źródło
Wersja dla Swift 5, która używa CryptoKit na iOS 13 i wraca do CommonCrypto w przeciwnym razie:
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()) } } }
Stosowanie:
let string = "The quick brown fox jumps over the lazy dog" let hexDigest = string.data(using: .ascii)!.sha256 assert(hexDigest == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592")
Dostępne również za pośrednictwem menedżera pakietów Swift:
https://github.com/ralfebert/TinyHashes
źródło
import CryptoKit
Ale czy przerwa na iOS 12 nie będzie? Jest to struktura wyłącznie dla systemu iOS 13.0+.#if canImport(CryptoKit)
do importu warunkowego. Nie zapomnij ustawić-weak_framework CryptoKit
inOther Linker Flags
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() } }
źródło
Oto moja prosta 3-wierszowa funkcja Swift 4 do tego wykorzystująca interfejs Security Transforms API, który jest częścią Foundation w systemie macOS. (Niestety programiści iOS nie mogą korzystać z tej techniki).
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 } }
źródło
Oto metoda wykorzystująca API CoreFoundation Security Transforms, więc nie musisz nawet łączyć się z CommonCrypto. Z jakiegoś powodu w 10.10 / Xcode 7 łączenie się z CommmonCrypto za pomocą Swift jest dramatem, więc zamiast tego użyłem tego.
Ta metoda odczytuje z pliku
NSInputStream
, który można uzyskać z pliku, lub utworzyć taki, który odczytujeNSData
, lub można utworzyć powiązane strumienie odczytu / zapisu dla procesu buforowanego.// 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() } }
źródło
Dla 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
źródło
Przetestowano w Swift5.
Jeśli chcesz uzyskać hash w String ,
tak właśnie zrobiłem.
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: "") }
źródło
Zbadałem wiele odpowiedzi i podsumowałem to:
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() } } }
źródło
Wolę używać:
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 jest zakodowany w base64.
źródło
W przypadku innych odpowiedzi wystąpią problemy z wydajnością obliczania skrótów z dużych ilości danych (np. Dużych plików). Nie będziesz chciał załadować wszystkich danych do pamięci naraz. Rozważ następujące podejście przy użyciu aktualizacji / finalizacji:
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) }) } }
Możesz go użyć w następujący sposób:
let digest = SHA256Digest() try digest.update(url: fileURL) let result = digest.finalize().hexString print(result)
źródło
Swift 5 przykładowy projekt open source z funkcją Alert Copy
https://github.com/devzhr/Swift-CryptoSHA256
źródło