38

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

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

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

4

11 回答 11

73

你可以在 Swift 中做到这一点。只需确保添加#import <CommonCrypto/CommonHMAC.h>到桥接 Objective-C 桥接头中即可。

更新:对于 Swift 4,请在此处使用 Swift 包管理器查看更好的解决方案:https ://github.com/jernejsstrasner/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)
    }

}
于 2014-06-25T14:33:46.070 回答
9

为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 <CommonCrypto/CommonHMAC.h>Header.h

于 2017-05-09T06:00:07.583 回答
4

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

将以下包含添加到您的-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,我只需用适当的方法替换该方法中的两个常量。就我而言,我需要kCCHmacAlgSHA256and CC_SHA256_DIGEST_LENGTH

感谢 Jernej Strasner 和其他人的其他答案,我只是想为我的案例做一些更简单的事情。

于 2016-03-18T05:00:24.290 回答
3

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

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

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

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

然后在斯威夫特:

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) 文件中。

于 2014-06-07T18:05:15.847 回答
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)
    }

}
于 2014-10-09T19:43:10.043 回答
3

对于 OS X(但在撰写本文时不适用于 iOS,当 9.3.1 是当前版本时),您可以使用 aSecTransform在没有桥接头和 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
于 2016-05-02T01:57:39.360 回答
3

谢谢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!

}
于 2017-06-24T13:30:32.300 回答
3

这是“如何在没有 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)")

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

于 2017-01-04T14:04:53.990 回答
2

在 swift 1.2 之后替换 func 摘要中的行

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

经过

let strLen = self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
let keyLen = key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
于 2015-04-14T14:00:47.620 回答
0

Github repo上的 Swift 4 版本演示

于 2018-02-28T02:11:09.153 回答
0

你可以在 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
于 2020-03-17T06:22:50.303 回答