【问题标题】:Implementing HMAC and SHA1 encryption in swift快速实现 HMAC 和 SHA1 加密
【发布时间】:2017-10-04 16:56:48
【问题描述】:

我对 Swift 比较陌生,我一直在使用 HMAC 和 SHA1 进行加密。我找到了以下答案https://stackoverflow.com/a/24411522/4188344,但我不知道如何正确实施。任何帮助都会很棒。

【问题讨论】:

  • 我已经使用了这个确切的答案,它对我有用。您能描述一下您遇到的问题吗?
  • 可能是复制并粘贴代码并在您的String 对象上使用–hmac(algorithm:key:) 方法...?
  • 我设法在 Objective-C 中找到了一个解决方案,我想我错过了相当于:@interface HashSHA1: NSObject - (NSString *) hashedValue :(NSString *) key andData : (NSString *) 数据;
  • HMAC 和 SHA1 不加密。可以从 Swift 使用 Common Crypto 并提供您需要的原语。
  • 您链接到的答案在 Swift 中,@maksimov 也提供了答案。将使用此代码的代码添加到问题中,有人可能会发现问题。

标签: ios swift xcode6


【解决方案1】:

问题解决了!首先我没有正确使用字符串函数......我最终得到了这个:

    let hmacResult:String = "myStringToHMAC".hmac(HMACAlgorithm.SHA1, key: "myKey")

然后我忘记了我需要对 hmac 结果进行 base64 编码。所以我将问题中链接的字符串函数修改为...

enum HMACAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    func toCCHmacAlgorithm() -> 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 hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
        let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
        var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
        var hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
        var hmacBase64 = hmacData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding76CharacterLineLength)
        return String(hmacBase64)
    }
}

这给了我正确的结果

lGCtbW+DNHFraNoxPGK3trgM/98=

【讨论】:

  • 谢谢!为我工作只需要导入
  • var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatValue: 0) 在新的 swift 中有错误
  • 你确定你得到了正确的结果吗?你不应该得到它,因为你在最后传递了一个额外的\0 的密钥和数据,因为你使用的是 cStrings (null 终止)
  • 没有得到相同的结果。我得到 85afQardtIk0rRWRlQ10qrL/STc=
【解决方案2】:

这是@David Wood 为 Swift 3 更新的解决方案:

enum HMACAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    func toCCHmacAlgorithm() -> 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 hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cString(using: String.Encoding.utf8)
        let cData = self.cString(using: String.Encoding.utf8)
        var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, Int(strlen(cKey!)), cData!, Int(strlen(cData!)), &result)
        let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
        let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)
        return String(hmacBase64)
    }
}

// usage:
let hmacResult: String = "myStringToHMAC".hmac(algorithm: HMACAlgorithm.SHA1, key: "foo")

【讨论】:

  • 我收到一个错误“参数计数必须在重复之前”。这个错误在 hmac 函数的第 3 行报告。我正在使用 Xcode 8.2.1 和 Swift 3。
  • @JayprakashDubey 听起来你有错字?我在几个项目中运行这个确切的代码。你提到的错误听起来像是在这个初始化器上,在上面的代码中是正确的:developer.apple.com/reference/swift/array/1641692-init#
【解决方案3】:

以下是创建 Swift 4 扩展的方法:

桥接头文件

#import <CommonCrypto/CommonCrypto.h>

代码

extension String {

    func hmac(key: String) -> String {
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, self, self.count, &digest)
        let data = Data(bytes: digest)
        return data.map { String(format: "%02hhx", $0) }.joined()
    }

}

示例

let result = "test".hmac(key: "test")

结果

0c94515c15e5095b8a87a50ba0df3bf38ed05fe6

【讨论】:

  • @user3745635 您需要导入 Apple 安全标头。查看更新后的帖子。
  • @sundance 因代码错误而被否决,与stackoverflow.com/a/27032056/887549 相比,数据中的俄罗斯符号给出了不同的结果
【解决方案4】:

如果您想要十六进制格式的相同结果,可以使用以下扩展名:

extension String {
    func hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
        let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
        var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
        let length : Int = Int(strlen(cKey!))
        let data : Int = Int(strlen(cData!))
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!,length , cData!, data, &result)

        let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))

        var bytes = [UInt8](count: hmacData.length, repeatedValue: 0)
        hmacData.getBytes(&bytes, length: hmacData.length)

        var hexString = ""
        for byte in bytes {
            hexString += String(format:"%02hhx", UInt8(byte))
        }
        return hexString
    }
}

【讨论】:

    【解决方案5】:

    我已将这个模块作为框架添加到我的项目中:

    https://github.com/CryptoCoinSwift/SHA256-Swift

    我还在SHA256.swift 中添加了以下String 扩展:

    public extension String {
    
        func sha256(key: String) -> String {
            let inputData: NSData = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
            let keyData: NSData = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    
            let algorithm = HMACAlgorithm.SHA256
            let digestLen = algorithm.digestLength()
            let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
    
            CCHmac(algorithm.toCCEnum(), keyData.bytes, UInt(keyData.length), inputData.bytes, UInt(inputData.length), result)
            let data = NSData(bytes: result, length: digestLen)
            result.destroy()
            return data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
        }
    
    }
    

    这种从String 生成base64 编码签名的方法可以这样完成:

    let signature: String = "\(payload)".sha256(secretKey)
    

    【讨论】:

    • 为什么要扩展一个简单的方法?这个方法真的属于 String 类的一部分吗?
    • 嗯,应该有比“为什么不”更好的理由。如果没有明确的理由和优势使其延期,那么不要这样做。需要回答的问题:“这是String 类的自然和预期的一部分吗?”我会期望在 String 类中找到所有加密的东西吗?
    • 当然,你是对的。就我而言,这是一个小型原型项目,而我刚开始使用 Swift 我就像“哦,看,我可以扩展 String 类并制作这个快捷方式,哇!”。在这个实验中没有人受伤。 :-)
    【解决方案6】:

    我已经检查了上面的答案,发现它很长。

    解决方案:我得到了第三方:IDZSwiftCommonCrypto

    使用吊舱:pod 'IDZSwiftCommonCrypto'

    并使用以下函数来实现所需的输出:

    func getHMacSHA1(forMessage message: String, key: String) -> String? {
        let hMacVal = HMAC(algorithm: HMAC.Algorithm.sha1, key: key).update(string: message)?.final()
        if let encryptedData = hMacVal {
            let decData = NSData(bytes: encryptedData, length: Int(encryptedData.count))
            let base64String = decData.base64EncodedString(options: .lineLength64Characters)
            print("base64String: \(base64String)")
            return base64String
        } else {
            return nil
        }
    }
    

    要查看结果,请使用以下网站:

    https://hash.online-convert.com/sha1-generator

    在 Swift 4.0 中测试

    【讨论】:

      【解决方案7】:

      对密钥和消息使用原始字节,而不是编码为 utf8:

          static func getHmac_X_Sha1() -> [UInt8] {
      
              let msg:[UInt8] = message_uint8;
              let msgLen = message_uint8.count;
              let digestLen = Int(CC_SHA1_DIGEST_LENGTH)
              let digest = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
              let keyStr:[UInt8] = key_uint8
              let keyLen = key_uint8.count
      
              CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyStr, keyLen, msg, msgLen, digest)
      
      
              //Build a hex string of result
              let hash_hex_string = NSMutableString()
              for i in 0..<digestLen {
                  hash_hex_string.appendFormat("%02x", result[i])
               }
               //print(hash_hex_string)
               result.deallocate()
      
               // Resolve hash_hex_string to byte array
               let hash_bytes:[UInt8] = hexStringToBytes(String(hash_hex_string))
               return hash_bytes
           }
      
      
      
          //Return a byte array from hex string input
           private static func hexStringToBytes(_ string: String) -> [UInt8]? {
              let length = string.characters.count
              if length & 1 != 0 {
                  return nil
              }
              var bytes = [UInt8]()
              bytes.reserveCapacity(length/2)
              var index = string.startIndex
              for _ in 0..<length/2 {
                  let nextIndex = string.index(index, offsetBy: 2)
                  if let b = UInt8(string[index..<nextIndex], radix: 16) {
                      bytes.append(b)
                  } else {
                      return nil
                  }
                  index = nextIndex
              }
              return bytes
           }
      

      【讨论】:

        【解决方案8】:

        在 Swift 4 中你需要库 CommonCrypto https://forums.developer.apple.com/thread/46477

        #import <CommonCrypto/CommonCrypto.h>
        

        您可以使用 base64 创建扩展

        extension String {
            func hmac(key: String) -> String {
                var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
                CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count, self, self.count, &digest)
                let data = Data(bytes: digest)
                return data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
            }
        }
        

        用途:

        print("HMAC_SHA256:".hmac(key: "MyKey"))
        

        结果:

        6GM2evJeNZYdP3OjPcKmg8TDzILSQAjy4NGhCHnBH5M=
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-10
        • 2013-05-22
        • 2017-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多