2014-09-10 18 views
29

Trong Objective-C nó trông như thế này:Làm thế nào để băm NSString với SHA1 trong Swift?

#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 

tôi cần một cái gì đó như thế này với Swift, là nó có thể?

Vui lòng hiển thị ví dụ về công việc.

Trả lời

102

Mã mục tiêu-C của bạn (sử dụng danh mục NSString) có thể được dịch trực tiếp sang Swift (sử dụng tiện ích mở rộng String).

Trước tiên, bạn phải tạo một "cầu nối tiêu đề" và thêm

#import <CommonCrypto/CommonCrypto.h> 

Sau đó:

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

này có thể được viết hơi ngắn hơn và mau lẹ như

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

Cập nhật cho 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("") 
    } 
} 

Để trở về một Base-64 chuỗi mã hóa thay vì một chuỗi hex mã hóa, chỉ cần thay thế

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

với

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

Cập nhật cho 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() 
    } 
} 

Để trở về một Base-64 chuỗi mã hóa thay vì một chuỗi hex mã hóa, chỉ cần thay thế

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

bởi

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

Có thể không sử dụng tiêu đề cầu nối obj-c không? Tôi tìm kiếm giải pháp Swift sạch. –

+0

@MihaelIsaev: Ý của bạn là "giải pháp Swift sạch"? Bạn có muốn * tái triển khai * các thuật toán trong Swift không? Tôi sẽ không khuyên bạn nên nó vì CommonCrypto là một giải pháp được thiết lập làm việc. Nhưng bạn có thể xem https://github.com/krzyzanowskim/CryptoSwift. –

+0

Vâng, tôi muốn một số thư viện mà tôi có thể sử dụng mà không có obj-c cầu nối. Thật không may https://github.com/krzyzanowskim/CryptoSwift nó đang sử dụng cầu nối obj-c quá: ( –

0

Vâng, nó có thể: làm cho rằng mã Objective-C thể truy cập từ nhanh chóng

Xem documentation.

Tôi sẽ tránh viết lại nhanh chóng nếu bạn không nhận được bất kỳ lợi ích nào (chẳng hạn như sử dụng các tính năng cụ thể nhanh).

Ngoài ra, trong một dự án tôi đang làm việc trên tôi đã sử dụng một số mã mục tiêu-c tương tự như của bạn để xử lý băm. Lúc đầu, tôi bắt đầu viết nó nhanh chóng, sau đó tôi nhận ra rằng nó chỉ đơn giản là tốt hơn và tốt hơn để tái sử dụng cũ tốt obj-c.

4

Để có được kết quả như NSData, với điều kiện bạn đã bao gồm <CommonCrypto/CommonCrypto.h> trong phần đầu cầu nối của bạn :

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

Cũng sử dụng phân bổ con trỏ thích hợp.Gọi nó như thế này:

myString.dataUsingEncoding(NSUTF8StringEncoding)?.sha1() 

Nếu bạn cần một đại diện hex của NSData đã xem answer khác của tôi.

+0

Có vẻ như có rò rỉ bộ nhớ. Bạn phải dealloc 'digest' hoặc sử dụng' NSData (bytesNoCopy: ..) '. –

+0

@MartinR có tất nhiên. –

1

Chúng ta có thể trích xuất logic để mã hóa chuỗi bằng sha1 cho ba bước:

  1. chuỗi Chuyển đổi đối tượng dữ liệu
  2. Mã hóa dữ liệu sử dụng chức năng SHA1 để dữ liệu
  3. Chuyển đổi đối tượng dữ liệu vào chuỗi hex

IMHO dễ đọc hơn nhiều và phiên bản này không yêu cầu 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

Lưu ý rằng một chuyển đổi thành UTF-8 * không thể * thất bại. –

Các vấn đề liên quan