Подтвердить что ты не робот

Как хэшировать NSString с SHA1 в Swift?

В 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, возможно ли это?

Пожалуйста, покажите пример работы.

4b9b3361

Ответ 1

Ваш код 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())

Это можно записать немного короче, а Swifter -.

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

Чтобы вернуть строку в кодировке 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()
    }
}

Чтобы вернуть строку в кодировке 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() теперь вызывает замыкание с UnsafeRawBufferPointer, а 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()
    }
}

Ответ 2

Чтобы получить результат как NSData, при условии, что вы включили <CommonCrypto/CommonCrypto.h> в свой заголовок моста:

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

Также использует правильное распределение указателя. Вызовите его вот так:

myString.dataUsingEncoding(NSUTF8StringEncoding)?.sha1()

Если вам нужно шестнадцатеричное представление NSData, посмотрите на мой другой ответ.

Ответ 3

Да, возможно, скопируйте этот класс в свой проект. https://github.com/idrougge/sha1-swift

И это будет легко, как:

 SHA1.hexString(from: "myPhrase" )!

Проверено на swift 3 и swift 4.

Ответ 4

С добавлением CryptoKit в iOS13 у нас теперь есть собственный API Swift:

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
}

Ответ 5

Мы можем извлечь логику для шифрования строки, используя sha1 для трех шагов:

  • Преобразование строки в объект данных
  • Шифровать данные с использованием функции SHA1 для данных
  • Преобразование объекта данных в шестнадцатеричную строку

IMHO это гораздо более читаемо, и эта версия не требует 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()
        }

    }

Ответ 6

Да, возможно: сделать код objective-c доступным из быстрого

См. documentation.

Я бы не стал переписывать его в swift, если вы не получите никакой пользы (например, с использованием особых функций).

Кроме того, в проекте, над которым я работаю, я использовал код objective-c, похожий на ваш, для обработки хэшей. В начале я начал писать его быстро, затем понял, что проще и лучше использовать старые добрые объекты.

Ответ 7

Версия для Swift 5, которая использует CryptoKit на iOS 13 и в противном случае использует 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 sha1: String {
        if #available(iOS 13.0, *) {
            return hexString(Insecure.SHA1.hash(data: self).makeIterator())
        } else {
            var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
            self.withUnsafeBytes { bytes in
                _ = CC_SHA1(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)!.sha1
assert(hexDigest == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")

Также доступно через менеджер пакетов Swift:
https://github.com/ralfebert/TinyHashes