신속한 SHA256
내 프로젝트에서 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
예를 들어,.
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)
}
최고의 답변이 저에게 효과가 없었습니다. 나는 웹에서 무언가를 발견하고 그것을 약간 변경했고 이제는 작동합니다 : 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를 가져 오는 브리징 헤더가 필요합니다. 계정이없는 경우 다음 단계를 따르세요.
- 새 파일 만들기-> 헤더 파일-> 다른 이름으로 저장
BridgingHeader
- 빌드 설정-> Objective-C 브리징 헤더-> 추가
ProjectName/BridgingHeader.h
#import <CommonCrypto/CommonHMAC.h>
헤더 파일에 넣기
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"
여기에 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
}
}
다음은 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()
}
}
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
함께 CryptoKit
iOS13 추가, 우리는 이제 기본 스위프트의 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
...
다음을 선호합니다.
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로 인코딩됩니다.
다른 답변은 대용량 데이터 (예 : 대용량 파일)에서 다이제스트를 계산하는 데 성능 문제가 있습니다. 모든 데이터를 한 번에 메모리로로드하지 않을 것입니다. 업데이트 / 완료를 사용하는 다음 접근 방식을 고려하십시오.
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)
참고 URL : https://stackoverflow.com/questions/25388747/sha256-in-swift
'Nice programing' 카테고리의 다른 글
Proguard 난독 화를 사용할 때 Gson EnumTypeAdapter의 AssertionError (0) | 2020.11.22 |
---|---|
UIImage가있는 UIBarButtonItem 항상 색조 iOS 7 (0) | 2020.11.22 |
Yii2에서 깨끗한 URL 사용 (0) | 2020.11.22 |
SQL Server 2005 쿼리를 CSV로 내보내는 방법 (0) | 2020.11.22 |
awk에서 행과 요소 선택 (0) | 2020.11.22 |