【问题标题】:CommonHMAC in SwiftSwift 中的 CommonHMAC
【发布时间】:2014-07-28 18:26:00
【问题描述】:

我正在尝试在 Swift 中创建一个字符串的 HMAC SHA-1 哈希,但由于它似乎没有导入 CommonCrypto 框架,所以无法弄清楚如何与 API 交互。我尝试了各种不同形式的“import CommonCrypto”并创建了一个桥接头文件,但都没有任何区别。

奇怪的是,如果我创建了一个 Objective-C 类,我可以毫无问题地与 API 交互,所以这似乎是 Swift 独有的。

另外,如果有人能告诉我 uint8_t digest[CC_SHA1_DIGEST_LENGTH] 在 Swift 中的等价物,我将不胜感激

【问题讨论】:

    标签: macos hash swift hmacsha1 commoncrypto


    【解决方案1】:

    Swift 可以弄清楚如何映射到 Obj-C 框架,但对于直接的 C 函数则不然。 Apple 为 GCD 和 AudioToolbox 之类的东西提供了一些绑定,但不是全部。 CommonCrypto 似乎还没有适当的绑定。

    对于这种情况,我建议您在 Obj-C 中编写自己的基本包装器,然后在 Swift 中使用这些包装器类。

    例如,您可以在 Obj-C 中创建一个HMAC 类:

    // This enum is in HMAC.h
    typedef NS_ENUM(NSInteger, HMACAlgorithm)
    {
        SHA1,
        MD5,
        SHA256,
        SHA384,
        SHA512,
        SHA224
    };
    
    // Class methods here
    + (NSData *)calculateWithAlgorithm:(HMACAlgorithm)algorithm forKey:(const void *)key andData:(const void *)data
    {
        NSInteger digestLength = [self digestLengthForAlgorithm:algorithm];
        unsigned char hmac[digestLength];
    
        CCHmac(algorithm, &key, strlen(key), &data, strlen(data), &hmac);
    
        NSData *hmacBytes = [NSData dataWithBytes:hmac length:sizeof(hmac)];
        return hmacBytes;
    }
    
    + (NSInteger)digestLengthForAlgorithm:(HMACAlgorithm)algorithm
    {
        switch (algorithm)
        {
            case MD5: return CC_MD5_DIGEST_LENGTH;
            case SHA1: return CC_SHA1_DIGEST_LENGTH;
            case SHA224: return CC_SHA224_DIGEST_LENGTH;
            case SHA256: return CC_SHA256_DIGEST_LENGTH;
            case SHA384: return CC_SHA384_DIGEST_LENGTH;
            case SHA512: return CC_SHA512_DIGEST_LENGTH;
            default: return 0;
        }
    }
    

    然后在 Swift 中:

    class SwiftHMAC
    {
        // Swift will automatically pull the enum from Obj-C
    
        func calculate(algorithm:HMACAlgorithm, key:Byte[], data:Byte[]) -> Byte[]
        {
            let computedHMAC = HMAC.calculateWithAlgorithm(algorithm, forKey: key, andData: data)
    
            var rawBytes = Byte[](count: computedHMAC.length, repeatedValue: 0)
            computedHMAC.getBytes(&rawBytes)
    
            return rawBytes
        }
    }
    

    请记住将#import "HMAC.h" 添加到您的 Swift 桥接头以及将#import "<##Your-Project-Name##>-Swift.h" 添加到 Obj-C 实现 (.m) 文件中。

    【讨论】:

    • 嗯...你能解释一下我最终如何使用calculate(algorithm:HMACAlgorithm, key:Byte[], data:Byte[]) -> Byte[] 获得字符串的MD5 吗?我确实创建了我的包装类并将其导入到 Swift,但我不知道如何从该方法中获取 MD5 散列。谢谢!
    • 这会产生一些奇怪的结果吗?运行以下命令:var data = "tohash".dataUsingEncoding(NSASCIIStringEncoding).bytes let key = "testing2".dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false).bytes let computedHMAC = HMAC.calculateWithAlgorithm(HMACAlgorithm.SHA256, forKey: key, andData: data) let hashString = NSString(data: computedHMAC, encoding: NSASCIIStringEncoding) println(hashString) 产生 «£#sübwôt5áW²¡Å^ø
    • @MihaiFratu 获取字符串的 MD5 哈希,请查看我在*.com/questions/24123518/…的答案
    • 酷,谢谢!最后,我在 NSString 上使用了旧的 Objective-C 类别以及我的 Swift 代码。但是你的解决方案也不错!
    • 您将 obj-c 移植到 swift 的方法是正确的,但是您的 objc-c HMAC 逻辑不如此处*.com/questions/8458917/… 给出的那么好。相信我,我花了 5 个小时尝试调整您的代码,以便我可以传入字符串,但它不起作用。
    【解决方案2】:

    您可以在 Swift 中完成。只需确保将 #import <CommonCrypto/CommonHMAC.h> 添加到桥接 Objective-C 桥接头中即可。

    更新:对于 Swift 4,请在此处使用 Swift 包管理器查看更好的解决方案:https://github.com/jernejstrasner/SwiftCrypto

    enum CryptoAlgorithm {
        case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
        var HMACAlgorithm: CCHmacAlgorithm {
            var result: Int = 0
            switch self {
            case .MD5:      result = kCCHmacAlgMD5
            case .SHA1:     result = kCCHmacAlgSHA1
            case .SHA224:   result = kCCHmacAlgSHA224
            case .SHA256:   result = kCCHmacAlgSHA256
            case .SHA384:   result = kCCHmacAlgSHA384
            case .SHA512:   result = kCCHmacAlgSHA512
            }
            return CCHmacAlgorithm(result)
        }
    
        var digestLength: Int {
            var result: Int32 = 0
            switch self {
            case .MD5:      result = CC_MD5_DIGEST_LENGTH
            case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
            case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
            case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
            case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
            case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
            }
            return Int(result)
        }
    }
    
    extension String {
    
        func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
            let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
            let strLen = Int(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
            let digestLen = algorithm.digestLength
            let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
            let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)
            let keyLen = Int(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    
            CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
    
            let digest = stringFromResult(result, length: digestLen)
    
            result.dealloc(digestLen)
    
            return digest
        }
    
        private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
            var hash = NSMutableString()
            for i in 0..<length {
                hash.appendFormat("%02x", result[i])
            }
            return String(hash)
        }
    
    }
    

    【讨论】:

    • 有趣。这是 Beta 2 中的新功能吗?我想我当时试过了,但也许我错了。感谢您的回复!
    • @MattDonnelly 可能是这样。我没有在 Beta 1 下尝试过。不过,我确实通过 Beta 1 中的桥接头成功导入了其他 C 级框架头文件。
    • 从 Swift 1.2 开始不工作。获取:无法使用类型为“(CCHmacAlgorithm,UnsafePointer,UInt,[CChar],UInt,UnsafeMutablePointer)”的参数列表调用“CCHmac”
    • @MasonG.Zhwiti 将 strLenkeyLen 更改为 Int 而不是 Uint 以使其在 Swift 1.2 中工作。
    • cStringUsingEncoding 已在 Swift 3 中移除。请改用 UnsafePointer&lt;Int8&gt;([Int8](string.utf8CString))UnsafePointer&lt;Int8&gt;([Int8](string.utf8))
    【解决方案3】:

    以下是@jernej-strasner 发布内容的更正版本

    enum HMACAlgorithm {
        case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
        func toCCEnum() -> CCHmacAlgorithm {
            var result: Int = 0
            switch self {
            case .MD5:
                result = kCCHmacAlgMD5
            case .SHA1:
                result = kCCHmacAlgSHA1
            case .SHA224:
                result = kCCHmacAlgSHA224
            case .SHA256:
                result = kCCHmacAlgSHA256
            case .SHA384:
                result = kCCHmacAlgSHA384
            case .SHA512:
                result = kCCHmacAlgSHA512
            }
            return CCHmacAlgorithm(result)
        }
    
        func digestLength() -> Int {
            var result: CInt = 0
            switch self {
            case .MD5:
                result = CC_MD5_DIGEST_LENGTH
            case .SHA1:
                result = CC_SHA1_DIGEST_LENGTH
            case .SHA224:
                result = CC_SHA224_DIGEST_LENGTH
            case .SHA256:
                result = CC_SHA256_DIGEST_LENGTH
            case .SHA384:
                result = CC_SHA384_DIGEST_LENGTH
            case .SHA512:
                result = CC_SHA512_DIGEST_LENGTH
            }
            return Int(result)
        }
    }
    
    extension String {
    
        func digest(algorithm: HMACAlgorithm, key: String) -> String! {
            let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
            let strLen = UInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
            let digestLen = algorithm.digestLength()
            let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
            let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)
            let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    
            CCHmac(algorithm.toCCEnum(), keyStr!, keyLen, str!, strLen, result)
    
            var hash = NSMutableString()
            for i in 0..<digestLen {
                hash.appendFormat("%02x", result[i])
            }
    
            result.destroy()
    
            return String(hash)
        }
    
    }
    

    【讨论】:

    【解决方案4】:

    在 swift 1.2 之后替换 func digest 中的行

    let strLen = UInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
    

    通过

    let strLen = self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
    let keyLen = key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
    

    【讨论】:

      【解决方案5】:

      我想保持最小化。避免创建一个可以处理所有不同摘要类型的泛型类的额外复杂性,而只需有一个小方法,如果需要我可以放入一个类中。我也更愿意避免向核心类添加扩展。

      将以下包含添加到您的 -Bridging-Header.h 文件中:

      #import <CommonCrypto/CommonHMAC.h>
      

      然后在需要调用CCHmac()的类中添加私有方法:

      private func hmac(string: NSString, key: NSData) -> NSData {
        let keyBytes = UnsafePointer<CUnsignedChar>(key.bytes)
        let data = string.cStringUsingEncoding(NSUTF8StringEncoding)
        let dataLen = Int(string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        let digestLen = Int(CC_SHA1_DIGEST_LENGTH)
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyBytes, key.length, data, dataLen, result);
        return NSData(bytes: result, length: digestLen)
      }
      

      如果我需要不同的CCHmacAlgorithm,我只需将该方法中的两个常量替换为适当的常量即可。就我而言,我需要kCCHmacAlgSHA256CC_SHA256_DIGEST_LENGTH

      感谢 Jernej Strasner 和其他人的其他答案,我只是想为我的案例提供一些更简单的东西。

      【讨论】:

        【解决方案6】:

        对于 OS X(但在撰写本文时不适用于 iOS,当 9.3.1 是当前版本时),您可以使用 SecTransform 在没有桥接头和 Objective-C 的 Swift 中计算 HMAC SHA-1。这是一个使用来自RFC 2202 中第一个 HMAC-SHA-1 测试用例的输入的示例:

        import Foundation
        import Security
        
        var error: Unmanaged<CFError>?
        let transform = SecDigestTransformCreate(kSecDigestHMACSHA1, 0, &error)
        let input = "Hi There"
        let inputData = input.dataUsingEncoding(NSUTF8StringEncoding)!
        let key = [UInt8](count: 20, repeatedValue: 0x0b)
        let keyData = key.withUnsafeBufferPointer { buffer in NSData(bytes: buffer.baseAddress, length: buffer.count) }
        SecTransformSetAttribute(transform, kSecTransformInputAttributeName, inputData, &error)
        SecTransformSetAttribute(transform, kSecDigestHMACKeyAttribute, keyData, &error)
        let outputData = SecTransformExecute(transform, &error) as! NSData
        

        【讨论】:

        • 看来'transform'指针需要CFReleased:developer.apple.com/reference/security/…。同样在我的一生中,我找不到在任何地方定义的 kSecDigestHMACSHA1(Swift 2.3,Xcode 8)。但希望将其用作纯 Swift 解决方案!
        • 我认为您不需要手动释放,因为SecDigestTransformCreate 不会返回UnmanagedkSecDigestHMACSHA1 常量在 macOS 的安全框架中声明。请记住,我的回答仅适用于 macOS,不适用于 iOS。
        • 哈哈,我看到然后没看到 OS X 评论。啊,我只是希望.. 谢谢!
        【解决方案7】:

        这是“如何在没有 Obj-c 桥接头的情况下在 Swift 项目中导入 CommonCrypto”的完整源代码,针对 Swift 3.0 进行了修改。实际的代码工作是“Mihael Isaev”。

        //
        //  HMAC.swift
        //
        //  Created by Mihael Isaev on 21.04.15.
        //  Copyright (c) 2014 Mihael Isaev inc. All rights reserved.
        //
        // ***********************************************************
        //
        // How to import CommonCrypto in Swift project without Obj-c briging header
        //
        // To work around this create a directory called CommonCrypto in the root of the project using Finder.
        // In this directory create a file name module.map and copy the following into the file.
        // You will need to alter the paths to ensure they point to the headers on your system.
        //
        // module CommonCrypto [system] {
        //     header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h"
        //     export *
        // }
        // To make this module visible to Xcode, go to Build Settings, Swift Compiler – Search Paths
        // and set Import Paths to point to the directory that contains the CommonCrypto directory.
        //
        // You should now be able to use import CommonCrypto in your Swift code.
        //
        // You have to set the Import Paths in every project that uses your framework so that Xcode can find it.
        //
        // ***********************************************************
        //
        // Modification for Swift 3.0 by Sanjay Sampat on 04.Jan.2017
        //
        // ***********************************************************
        
        import Foundation
        import CommonCrypto
        
        extension String {
            var md5: String {
            return HMAC.hash(inp: self, algo: HMACAlgo.MD5)
        }
        
        var sha1: String {
            return HMAC.hash(inp: self, algo: HMACAlgo.SHA1)
        }
        
        var sha224: String {
            return HMAC.hash(inp: self, algo: HMACAlgo.SHA224)
        }
        
        var sha256: String {
            return HMAC.hash(inp: self, algo: HMACAlgo.SHA256)
        }
        
        var sha384: String {
            return HMAC.hash(inp: self, algo: HMACAlgo.SHA384)
        }
        
        var sha512: String {
            return HMAC.hash(inp: self, algo: HMACAlgo.SHA512)
        }
        
        func aesEncrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {
            if let keyData = key.data(using: String.Encoding.utf8),
                let data = self.data(using: String.Encoding.utf8),
                let cryptData    = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
        
        
                    let keyLength              = size_t(kCCKeySizeAES128)
                    let operation: CCOperation = UInt32(kCCEncrypt)
                    let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
                    let options:   CCOptions   = UInt32(options)
        
        
        
                var numBytesEncrypted :size_t = 0
        
                let base64cryptStringOut = keyData.withUnsafeBytes {(keyBytes: UnsafePointer<CChar>)->String? in
                    return data.withUnsafeBytes {(dataBytes: UnsafePointer<CChar>)->String? in
        
                        let cryptStatus = CCCrypt(operation,
                                                  algoritm,
                                                  options,
                                                  keyBytes, keyLength,
                                                  iv,
                                                  dataBytes, data.count,
                                                  cryptData.mutableBytes, cryptData.length,
                                                  &numBytesEncrypted)
        
                        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
                            cryptData.length = Int(numBytesEncrypted)
                            let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
                            return base64cryptString
        
        
                        }
                        else {
                            return nil
                        }
                    }
                }
                return base64cryptStringOut
            }
            return nil
        }
        
        func aesDecrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {
            if let keyData = key.data(using: String.Encoding.utf8),
                let data = NSData(base64Encoded: self, options: .ignoreUnknownCharacters),
                let cryptData    = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) {
        
                    let keyLength              = size_t(kCCKeySizeAES128)
                    let operation: CCOperation = UInt32(kCCDecrypt)
                    let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
                    let options:   CCOptions   = UInt32(options)
        
                    var numBytesEncrypted :size_t = 0
        
                let unencryptedMessageOut = keyData.withUnsafeBytes {(keyBytes: UnsafePointer<CChar>)->String? in
                    let cryptStatus = CCCrypt(operation,
                        algoritm,
                        options,
                        keyBytes, keyLength,
                        iv,
                        data.bytes, data.length,
                        cryptData.mutableBytes, cryptData.length,
                        &numBytesEncrypted)
        
                    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
                        cryptData.length = Int(numBytesEncrypted)
                        let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8)
                        return unencryptedMessage
                    }
                    else {
                        return nil
                    }
                }
                return unencryptedMessageOut
            }
            return nil
        }
        }
        
        public struct HMAC {
        
        static func hash(inp: String, algo: HMACAlgo) -> String {
            if let stringData = inp.data(using: String.Encoding.utf8, allowLossyConversion: false) {
                return hexStringFromData(input: digest(input: stringData as NSData, algo: algo))
            }
            return ""
        }
        
        private static func digest(input : NSData, algo: HMACAlgo) -> NSData {
            let digestLength = algo.digestLength()
            var hash = [UInt8](repeating: 0, count: digestLength)
            switch algo {
            case .MD5:
                CC_MD5(input.bytes, UInt32(input.length), &hash)
                break
            case .SHA1:
                CC_SHA1(input.bytes, UInt32(input.length), &hash)
                break
            case .SHA224:
                CC_SHA224(input.bytes, UInt32(input.length), &hash)
                break
            case .SHA256:
                CC_SHA256(input.bytes, UInt32(input.length), &hash)
                break
            case .SHA384:
                CC_SHA384(input.bytes, UInt32(input.length), &hash)
                break
            case .SHA512:
                CC_SHA512(input.bytes, UInt32(input.length), &hash)
                break
            }
            return NSData(bytes: hash, length: digestLength)
        }
        
        private static func hexStringFromData(input: NSData) -> String {
            var bytes = [UInt8](repeating: 0, count: input.length)
            input.getBytes(&bytes, length: input.length)
        
            var hexString = ""
            for byte in bytes {
                hexString += String(format:"%02x", UInt8(byte))
            }
        
            return hexString
        }
        
        }
        
        enum HMACAlgo {
        case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
        
        func digestLength() -> Int {
            var result: CInt = 0
            switch self {
            case .MD5:
                result = CC_MD5_DIGEST_LENGTH
            case .SHA1:
                result = CC_SHA1_DIGEST_LENGTH
            case .SHA224:
                result = CC_SHA224_DIGEST_LENGTH
            case .SHA256:
                result = CC_SHA256_DIGEST_LENGTH
            case .SHA384:
                result = CC_SHA384_DIGEST_LENGTH
            case .SHA512:
                result = CC_SHA512_DIGEST_LENGTH
            }
            return Int(result)
        }
        }
        

        以下是使用示例。

            // TEST for Encryption and Decryption through HMAC Swift 3.0
            let iv = "iv-salt-Sanjay--" // fixed 16 chars.
            let cryptoKeyString = "01234567890123456789012345678901"
            let originalString = "My Name is Sanjay Sampat, Password is IL0ve2view2Kill@4#"
            print("Original String: \(originalString)")
            if let encodedString = originalString.aesEncrypt(key: cryptoKeyString, iv: iv){
                print("String Encoded: \(encodedString)")
                if let decryptedString = encodedString.aesDecrypt(key: cryptoKeyString, iv: iv)
                {
                    print("String Decoded: \(decryptedString)")
                }
                else{
                    print("Decoding failed")
                }
            }
            else{
                print("Encoding failed")
            }
        
        
            // Example To create sha1 from string
            let testString = "This is string to test sha1 hash string."
            let sha1Digest = testString.sha1
            print("sha1-hash-string: \(sha1Digest)")
        

        我希望这可以成为像我这样的用户的现成参考。 :)

        【讨论】:

          【解决方案8】:

          Swift 3.1 上试试这个:

          enum CryptoAlgorithm {
              case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
          
              var HMACAlgorithm: CCHmacAlgorithm {
                  var result: Int = 0
                  switch self {
                  case .MD5:      result = kCCHmacAlgMD5
                  case .SHA1:     result = kCCHmacAlgSHA1
                  case .SHA224:   result = kCCHmacAlgSHA224
                  case .SHA256:   result = kCCHmacAlgSHA256
                  case .SHA384:   result = kCCHmacAlgSHA384
                  case .SHA512:   result = kCCHmacAlgSHA512
                  }
                  return CCHmacAlgorithm(result)
              }
          
              var digestLength: Int {
                  var result: Int32 = 0
                  switch self {
                  case .MD5:      result = CC_MD5_DIGEST_LENGTH
                  case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
                  case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
                  case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
                  case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
                  case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
                  }
                  return Int(result)
              }
          }
          
          extension String {
          
              func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
                  let str = self.cString(using: String.Encoding.utf8)
                  let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
                  let digestLen = algorithm.digestLength
                  let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
                  let keyStr = key.cString(using: String.Encoding.utf8)
                  let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
          
                  CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
          
                  let digest = stringFromResult(result: result, length: digestLen)
          
                  result.deallocate(capacity: digestLen)
          
                  return digest
              }
          
              private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
                  let hash = NSMutableString()
                  for i in 0..<length {
                      hash.appendFormat("%02x", result[i])
                  }
                  return String(hash)
              }
          
          }
          

          别忘了将#import &lt;CommonCrypto/CommonHMAC.h&gt; 添加到Header.h

          【讨论】:

          • header.h 在哪里?
          • 这给出了错误:未声明的类型 CCHmacAlgorithm
          【解决方案9】:

          感谢Jernej Strasner 的精彩回答。在这里,我只是更新了他对 Swift 3.1 的回答:

          enum CryptoAlgorithm {
          case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
          
           var HMACAlgorithm: CCHmacAlgorithm {
              var result: Int = 0
              switch self {
              case .MD5:      result = kCCHmacAlgMD5
              case .SHA1:     result = kCCHmacAlgSHA1
              case .SHA224:   result = kCCHmacAlgSHA224
              case .SHA256:   result = kCCHmacAlgSHA256
              case .SHA384:   result = kCCHmacAlgSHA384
              case .SHA512:   result = kCCHmacAlgSHA512
              }
              return CCHmacAlgorithm(result)
           }
          
           var digestLength: Int {
              var result: Int32 = 0
              switch self {
              case .MD5:      result = CC_MD5_DIGEST_LENGTH
              case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
              case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
              case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
              case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
              case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
              }
              return Int(result)
           }
          }
          
          extension String {
          
          func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
              let str = self.cString(using: String.Encoding.utf8)
              let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
              let digestLen = algorithm.digestLength
              let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
              let keyStr = key.cString(using: String.Encoding.utf8)
              let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
          
              CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
          
              let digest = stringFromResult(result: result, length: digestLen)
          
              result.deallocate(capacity: digestLen)
          
              return digest
          }
          
          private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
              let hash = NSMutableString()
              for i in 0..<length {
                  hash.appendFormat("%02x", result[i])
              }
              return String(hash)
          }
          

          也可以使用它:

          func sha256(StringToSign : String, secretKey : String) -> String{
          
              let hex = StringToSign.hmac(algorithm: .SHA256, key: secretKey)
              let hexData = hex.data(using: String.Encoding.utf8)
              let finalString = hexData?.base64EncodedString(options: [.lineLength64Characters])
          
              return finalString!
          
          }
          

          【讨论】:

            【解决方案10】:

            Github 上的 Swift 4 版本演示 repo

            【讨论】:

              【解决方案11】:

              您可以在 Swift 中完成。只需确保将#import 添加到桥接 Objective-C 桥接头中即可。

              你可以在 GitHub 看到它ZYCrypto

              代码

              import Foundation
              
              extension String {
                  func hmac(by algorithm: Algorithm, key: [UInt8]) -> [UInt8] {
                      var result = [UInt8](repeating: 0, count: algorithm.digestLength())
                      CCHmac(algorithm.algorithm(), key, key.count, self.bytes, self.bytes.count, &result)
                      return result
                  }
              
                  func hashHex(by algorithm: Algorithm) -> String {
                      return algorithm.hash(string: self).hexString
                  }
              
                   func hash(by algorithm: Algorithm) -> [UInt8] {
                      return algorithm.hash(string: self)
                   }
              }
              
              
              enum Algorithm {
                  case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
              
                  func algorithm() -> CCHmacAlgorithm {
                      var result: Int = 0
                      switch self {
                      case .MD5:
                          result = kCCHmacAlgMD5
                      case .SHA1:
                          result = kCCHmacAlgSHA1
                      case .SHA224:
                          result = kCCHmacAlgSHA224
                      case .SHA256:
                          result = kCCHmacAlgSHA256
                      case .SHA384:
                          result = kCCHmacAlgSHA384
                      case .SHA512:
                          result = kCCHmacAlgSHA512
                      }
                      return CCHmacAlgorithm(result)
                  }
              
                  func digestLength() -> Int {
                      var result: CInt = 0
                      switch self {
                      case .MD5:
                          result = CC_MD5_DIGEST_LENGTH
                      case .SHA1:
                          result = CC_SHA1_DIGEST_LENGTH
                      case .SHA224:
                          result = CC_SHA224_DIGEST_LENGTH
                      case .SHA256:
                          result = CC_SHA256_DIGEST_LENGTH
                      case .SHA384:
                          result = CC_SHA384_DIGEST_LENGTH
                      case .SHA512:
                          result = CC_SHA512_DIGEST_LENGTH
                      }
                      return Int(result)
                  }
              
                  func hash(string: String) -> String {
                      var hash = [UInt8](repeating: 0, count: self.digestLength())
                      switch self {
                      case .MD5:
                          CC_MD5(string.bytes, CC_LONG(string.bytes.count), &hash)
                      case .SHA1:
                          CC_SHA1(string.bytes, CC_LONG(string.bytes.count), &hash)
                      case .SHA224:
                          CC_SHA224(string.bytes, CC_LONG(string.bytes.count), &hash)
                      case .SHA256:
                          CC_SHA256(string.bytes, CC_LONG(string.bytes.count), &hash)
                      case .SHA384:
                          CC_SHA384(string.bytes, CC_LONG(string.bytes.count), &hash)
                      case .SHA512:
                          CC_SHA512(string.bytes, CC_LONG(string.bytes.count), &hash)
                      }
                      return hash.hexString
                  }
              }
              
              extension Array where Element == UInt8 {
                  var hexString: String {
                      return self.reduce(""){$0 + String(format: "%02x", $1)}
                  }
              
                  var base64String: String {
                      return self.data.base64EncodedString(options: Data.Base64EncodingOptions.lineLength76Characters)
                  }
              
                  var data: Data {
                      return Data(self)
                  }
              }
              
              extension String {
                  var bytes: [UInt8] {
                      return [UInt8](self.utf8)
                  }
              }
              
              extension Data {
                  var bytes: [UInt8] {
                      return [UInt8](self)
                  }
              }
              

              如何使用

              HMAC
              let tData = "test string".hmac(by: .SHA256, key: "key string".bytes)
              let oDada = "other test string".hamc(by: .SHA256, key: tData)
              let signature = oData.hexSting.lowercased()
              
              哈希
              let sha256Hash = "test string".hashHex(by: .SHA256)
              let md5Hash = "test string".hash(by: .MD5).base64String
              

              【讨论】:

                最近更新 更多