2014-09-10 52 views
29

在Objective-C,它看起來是這樣的:如何使用SHA1在Swift中散列NSString?

#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 

我需要像這樣以快速的,這可能嗎?

請顯示工作示例。

回答

102

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

要返回Base64編碼串,而不是一個十六進制編碼的字符串, 只需更換

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

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

更新夫特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() 
    } 
} 

要返回Base64編碼字符串,而不是一個十六進制編碼字符串, 只需更換

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

通過

 return Data(bytes: digest).base64EncodedString() 
+1

是否有可能不使用obj-c橋接頭?我在尋找乾淨的Swift解決方案。 – 2014-12-16 18:32:23

+0

@MihaelIsaev:你的意思是「乾淨的Swift解決方案」?你想在Swift中重新實現算法嗎?我不會推薦它,因爲CommonCrypto是一個可行的解決方案。但你可以看看https://github.com/krzyzanowskim/CryptoSwift。 – 2014-12-16 18:48:56

+0

是的,我想要一些我可以在沒有obj-c橋接的情況下使用的庫。不幸的是https://github.com/krzyzanowskim/CryptoSwift它使用OBJ-C橋接太:( – 2014-12-16 19:46:52

0

是的,這是可能的:作出這樣的Objective-C代碼從迅速

訪問見documentation

如果你沒有得到任何好處(比如使用swift特有的特性),我會避免在swift中重寫它。

此外,在我正在開發的一個項目中,我使用了一些類似於您的Objective-C代碼來處理哈希。一開始我開始寫速度很快,然後我意識到重用舊的好obj-c會更容易,更好。

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

也使用適當的指針分配。如果你需要一個NSData十六進制表示看看我的其他answer

myString.dataUsingEncoding(NSUTF8StringEncoding)?.sha1() 

:調用它像這樣。

+0

似乎有內存泄漏你要的dealloc'digest',或使用'NSData的。(bytesNoCopy:..)' – 2015-04-20 21:05:43

+0

@MartinR是的,當然。忘記noCopy。謝謝! – 2015-04-21 05:34:43

1

我們可以提取用於使用SHA1三步加密串的邏輯:

  1. 字符串轉換爲數據對象
  2. 加密數據使用SHA1函數到數據
  3. 轉換數據對象到十六進制字符串

恕我直言,這是更具可讀性和這個版本並不需要的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() 
     } 

    } 
+0

請注意,轉換爲UTF-8 *不能*失敗。 – 2017-08-15 11:08:38