如何在Swift中使用SHA1散列NSString?

时间:2014-09-10 09:02:18

标签: swift sha1

在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这样的东西,有可能吗?

请显示工作示例。

7 个答案:

答案 0 :(得分:143)

您的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()
    }
}

答案 1 :(得分:4)

要将结果设为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的十六进制表示,请查看我的其他answer

答案 2 :(得分:4)

是的,可以将此课程复制到您的项目中。 https://github.com/idrougge/sha1-swift

这很简单:

 SHA1.hexString(from: "myPhrase" )!

测试swift 3和swift 4。

答案 3 :(得分:3)

在iOS13中添加了CryptoKit之后,我们现在有了本机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
}

答案 4 :(得分:1)

我们可以使用sha1提取用于加密字符串的逻辑,分三个步骤:

  1. 将字符串转换为数据对象
  2. 使用SHA1功能将数据加密到数据
  3. 将数据对象转换为十六进制字符串
  4. 恕我直言,它的可读性更高,此版本不需要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()
            }
    
        }
    

答案 5 :(得分:0)

是的,它是可能的:使得可以从swift

访问该Objective-c代码

documentation

如果您没有获得任何好处(例如使用特定于swift的功能),我会避免在swift中重写它。

此外,在我正在开发的项目中,我使用了一些与你类似的Objective-c代码来处理哈希。一开始我开始用swift编写它,然后我意识到重用旧的好的obj-c更容易也更好。

答案 6 :(得分:0)

Swift 5的一个版本,该版本在iOS 13上使用CryptoKit,否则回落到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