快速实现 HMAC 和 SHA1 加密

问题描述 投票:0回答:9

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

ios swift xcode6
9个回答
36
投票

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

    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=

18
投票

这是@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")

13
投票

以下是创建 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

9
投票

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

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

7
投票

我使用了这个模块作为框架添加到我的项目中:

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

我还添加了以下

String
扩展到
SHA256.swift

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)

0
投票

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

解决方案:我有第三方: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 中测试


0
投票

使用原始字节作为密钥和消息,而不是编码为 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
     }

0
投票

在 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=

0
投票

看起来你现在可以使用 Apple 的 CryptoKit 框架来做到这一点:

import CryptoKit

extension String {
    func getSignature(key: String, params: String) -> String {
        guard
            let secret = key.data(using: .utf8),
            let what = params.data(using: .utf8) else {
            fatalError("...")
        }
        var hmac = HMAC<Insecure.SHA1>(key: SymmetricKey(data: secret))
        hmac.update(data: what)
        let mac = Data(hmac.finalize())
        return mac.base64EncodedString()
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.