신속한 SHA256


82

내 프로젝트에서 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)

빠른 변환 할 수 없기 때문에 그것은 나에게 오류 모든 것을 제공 IntCC_LONG예를 들어,.


2
swift에서 ObjectiveC 메서드를 직접 호출 할 수 있습니다. 정확히 어디에 갇혀 있습니까?
Benjamin Gruenbaum

7
한 언어에서 다른 언어로 번역하는 것에 대한 질문은 주제를 벗어 났습니까? 언제부터?
Caleb

@BenjaminGruenbaum 문제는 "unsigned char hash [CC_SHA1_DIGEST_LENGTH];"문자열에 있습니다.
Yury Alexandrov

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

다른 문제는 지능이 CC_LONG로 변환하지 않는 것입니다
Yury 알렉산드로

답변:


127

Swift는 (Objective-) C 에서처럼 암시 적 변환을하지 않기 때문에 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
}

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

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
그것은 내가 그것주고 나에게 잘못된 값을 변환하려고 할 때처럼 내가 문자열이있는 NSData로 변환 할 수있는 방법
카말 Upasena을

좋은 대답! FYI API는 이제 Xcode 8.2.1에서 Int에서 반복하는 대신 repeatElement ...입니다.
iOS Gamer

@iOSGamer : 위의 Swift 3 버전이 올 바르고 Xcode 8.2.1에서 컴파일되는지 다시 확인했습니다. :)
Martin R

4
수 있도록이 솔루션에 추가으로서 CC_SHA256_DIGEST_LENGTH, CC_SHA256그리고 CC_LONG스위프트의 작업, 당신은 추가해야 #import <CommonCrypto/CommonDigest.h>브릿 징 헤더 파일.
Abion47

3
Swift 5 예제가 오래되었습니다.
Claus Jørgensen

77

최고의 답변이 저에게 효과가 없었습니다. 나는 웹에서 무언가를 발견하고 그것을 약간 변경했고 이제 작동합니다 : D. Swift 3 및 4 용입니다.

이 확장을 프로젝트 어딘가에 놓고 다음과 같은 문자열에 사용하십시오. 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
    }

}

그런데 CommonCrypto를 가져 오는 브리징 헤더가 필요합니다. 계정이없는 경우 다음 단계를 따르세요.

  1. 새 파일 만들기-> 헤더 파일-> 다른 이름으로 저장 BridgingHeader
  2. 빌드 설정-> Objective-C 브리징 헤더-> 추가 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

이 확장을 Framework Project에 추가 할 수 있습니까? Objective-C Bridging Header를 Framework 프로젝트에 어떻게 만들 수 있습니까?
ChandreshKanetiya

이 기능을 NSData 인스턴스에 사용할 수 있습니까? let data = NSData(contentsOfFile: "/Users/danila/metaprogramming-ruby-2.pdf") data.sha256()
Danila Kulakov

21

함께 CryptoKitiOS13 추가, 우리는 이제 기본 스위프트의 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
}

유틸이 프로토콜에 정의되어 있기 때문에 Digest모든 입력을 소화을 위해, 당신이 그것을 사용할 수 있습니다 CryptoKit, 같은 SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...


좋은 대답이지만 대상 버전이 mni 10 iOS13이어야합니다. iOS 버전에 따라이 솔루션과 수동 컴퓨팅을 모두 사용해야했습니다.
touti

차이점이 있습니까? var hexString: String { self.map { String(format: "%02hhx", $0) }.joined() }
muhasturk

솔루션은 작동하지만 Xcode의이 문제로 인해 iOS 11보다 낮은 대상으로 릴리스 구성에서 컴파일 할 수 없습니다. openradar.appspot.com/7495817
Vitalii

17

NSData& String(Swift 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"

이 부분에서이 오류가 발생했습니다. [let data = str.data (using : String.Encoding.utf8)]-> 오류 : 'Data'유형의 값을 예상 인수 유형 'String'으로 변환 할 수 없습니다. 내 나는 내가 잘못을하고있는 중이 야 알고하십시오
Kushal 쉬 레스타

브리징 헤더에 추가 했습니까? 이 코드는 Swift 3-ish에서 4.1로 변경되지 않았습니다. (Xcode 9.3 빌드).
Graham Perks

1
이것은 올바른 해시를 제공하지 않습니다. 온라인 SHA 생성기를 통해 직접 확인하십시오.
Frédéric Adda

아마도 온라인 생성기가 종료 0을 포함하여 작업을 수행합니까? 온라인 SHA256 또는 SHA-1 또는 SHA-2를 확인하고 있습니까?
Graham Perks

12

iOS 13에서 CryptoKit을 사용하고 그렇지 않으면 CommonCrypto로 대체하는 Swift 5 용 버전 :

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

Swift 패키지 관리자를 통해서도 사용 가능 :
https://github.com/ralfebert/TinyHashes


1
import CryptoKit그래도 iOS 12 에서는 중단 되지 않습니까? iOS 13.0+ 전용 프레임 워크입니다.
Kevin Renskers 2011

1
@KevinRenskers Use는 #if canImport(CryptoKit)조건부 가져 오기에 사용할 수 있습니다 . 세트 세트 잊지 마세요 -weak_framework CryptoKitOther Linker Flags
touti

iOS12 이하에서 작동하지 않고 위의 제안을 따랐지만 앱이 시작될 때 "라이브러리가로드되지 않음 : /System/Library/Frameworks/CryptoKit.framework/CryptoKit"이 계속 표시됩니다.
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()
  }
}

이전 버전과의 호환성이 필요한 경우 작동합니다. 다른 솔루션에서 제안한대로 CryptoKit를 가져 오면 앱이 시작될 때 "라이브러리가로드되지 않음 : /System/Library/Frameworks/CryptoKit.framework/CryptoKit"오류와 함께 iOS12 이하에서 앱이 충돌합니다.
Fede Henze

5

다음은 macOS 기반 Foundation의 일부인 Security Transforms API를 사용하는 간단한 3 줄 Swift 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
iOS에 대한 사랑이 전혀 없을 때까지 이것은 멋지게 보였습니다.
Zack Shapiro 2017

4

다음은 CoreFoundation Security Transforms API를 사용하는 방법이므로 CommonCrypto에 연결할 필요도 없습니다. 어떤 이유로 인해 10.10 / Xcode 7에서 CommmonCrypto와 Swift를 연결하는 것은 드라마이므로 대신 이것을 사용했습니다.

이 메서드는에서 읽습니다 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()
    }
}

내가 이해 한 바에 따르면 iOS가 아닌 OSX에서만 사용할 수 있습니다.
zaph

3

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

Swift5에서 테스트되었습니다.

해시 를 String 으로 얻고 싶다면 ,

이것이 내가 한 방법입니다.

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

1

나는 많은 답변을 조사하고 요약했습니다.

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

해시 된 문자열은 base64로 인코딩됩니다.


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)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.