IT Share you

Swift에서 SHA1로 NSString을 해시하는 방법은 무엇입니까?

shareyou 2021. 1. 9. 10:52
반응형

Swift에서 SHA1로 NSString을 해시하는 방법은 무엇입니까?


Objective-c에서는 다음과 같이 보입니다.

#include <sys/xattr.h>

@implementation NSString (reverse)

-(NSString*)sha1
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(data.bytes, (int)data.length, digest);
    NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

@end

Swift로 이와 같은 것이 필요합니다. 가능합니까?

작품 예를 보여주세요.


Objective-C 코드 ( NSString카테고리 사용)를 Swift로 직접 변환 할 수 있습니다 ( String확장 프로그램 사용).

먼저 "브리징 헤더"를 만들고

#import <CommonCrypto/CommonCrypto.h>

그때:

extension String {
    func sha1() -> String {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
        let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
        for byte in digest {
            output.appendFormat("%02x", byte)
        }
        return output as String
    }
}

println("Hello World".sha1())

이것은 약간 짧고 신속하게 작성할 수 있습니다.

extension String {
    func sha1() -> String {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
        let hexBytes = map(digest) { String(format: "%02hhx", $0) }
        return "".join(hexBytes)
    }
}

Swift 2 업데이트 :

extension String {
    func sha1() -> String {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joinWithSeparator("")
    }
}

16 진수로 인코딩 된 문자열 대신 Base-64로 인코딩 된 문자열을 반환하려면

        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joinWithSeparator("")

        return NSData(bytes: digest, length: digest.count).base64EncodedStringWithOptions([])

Swift 3 업데이트 :

extension String {
    func sha1() -> String {
        let data = self.data(using: String.Encoding.utf8)!
        var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
        data.withUnsafeBytes { 
            _ = CC_SHA1($0, CC_LONG(data.count), &digest)
        }
        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joined()
    }
}

16 진수로 인코딩 된 문자열 대신 Base-64로 인코딩 된 문자열을 반환하려면

        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joined()

으로

        return Data(bytes: digest).base64EncodedString()

Swift 4 업데이트 :

브리징 헤더 파일은 더 이상 필요하지 않으며 import CommonCrypto대신 다음을 수행 할 수 있습니다 .

import CommonCrypto

extension String {
    func sha1() -> String {
        let data = Data(self.utf8)
        var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
        data.withUnsafeBytes { 
            _ = CC_SHA1($0, CC_LONG(data.count), &digest)
        }
        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joined()
    }
}

Swift 5 업데이트 :

Data.withUnsafeBytes()메서드는 이제 UnsafeRawBufferPointerto를 baseAddress사용 하여 클로저를 호출 하고 C 함수에 초기 주소를 전달하는 데 사용됩니다.

import CommonCrypto

extension String {
    func sha1() -> String {
        let data = Data(self.utf8)
        var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
        data.withUnsafeBytes { 
            _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
        }
        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joined()
    }
}

To get the result as NSData, provided that you included <CommonCrypto/CommonCrypto.h> in your bridging header:

extension NSData {

    func sha1() -> NSData? {
        let len = Int(CC_SHA1_DIGEST_LENGTH)
        let digest = UnsafeMutablePointer<UInt8>.alloc(len)
        CC_SHA1(bytes, CC_LONG(length), digest)
        return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(digest), length: len)
    }
}

Also uses proper pointer allocation. Invoke it like this:

myString.dataUsingEncoding(NSUTF8StringEncoding)?.sha1()

If you need a hex representation of NSData have a look at my other answer.


Yes, it's possible, copy this class into your project. https://github.com/idrougge/sha1-swift

And it will be easy like:

 SHA1.hexString(from: "myPhrase" )!

Tested for swift 3 and swift 4.


With CryptoKit added in iOS13, we now have native 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 = Insecure.SHA1.hash(data: data)
    print(digest.data) // 20 bytes
    print(digest.hexStr) // 2AAE6C35C94FCFB415DBE95F408B9CE91EE846ED
}

We can extract logic for encrypting string using sha1 for three steps:

  1. Convert string to Data object
  2. Encrypt data using SHA1 function to Data
  3. Convert data object to hex string

IMHO it's much more readable and this version doesn't require NSData.

    extension String {

        var sha1: String {
            guard let data = data(using: .utf8, allowLossyConversion: false) else {
                // Here you can just return empty string or execute fatalError with some description that this specific string can not be converted to data
            }
            return data.digestSHA1.hexString
        }

    }

    fileprivate extension Data {

        var digestSHA1: Data {
            var bytes: [UInt8] = Array(repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))

            withUnsafeBytes {
                _ = CC_SHA1($0, CC_LONG(count), &bytes)
            }

            return Data(bytes: bytes)
        }

        var hexString: String {
            return map { String(format: "%02x", UInt8($0)) }.joined()
        }

    }

Yes, it's possible: make that objective-c code accessible from swift

See documentation.

I would avoid rewriting it in swift if you won't get any benefit (such as using swift-specific features).

Also, in a project I am working on I used some objective-c code similar to yours to handle hashes. At beginning I started writing it in swift, then I realized that it was just easier and better to reuse old good obj-c.

ReferenceURL : https://stackoverflow.com/questions/25761344/how-to-hash-nsstring-with-sha1-in-swift

반응형