How to replicate hash_hmac('sha256', $key, $secret_key) function in Swift 4?

后端 未结 2 904
暗喜
暗喜 2021-01-14 16:35

I\'ve tried generating the hash_hmac(\'sha256\', $key, $secret_key) php function equivalent in Swift 4 without success, after using libraries like CommonCrypto, CryptoSwift.

相关标签:
2条回答
  • 2021-01-14 16:48

    Swift 3/4:

    HMAC with MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3)

    These functions will hash either String or Data input with one of eight cryptographic hash algorithms.

    The name parameter specifies the hash function name as a String Supported functions are MD5, SHA1, SHA224, SHA256, SHA384 and SHA512

    This example requires Common Crypto
    It is necessary to have a bridging header to the project:
    #import <CommonCrypto/CommonCrypto.h>
    Add the Security.framework to the project.

    These functions takes a hash name, message to be hashed, a key and return a digest:


    hashName: name of a hash function as String  
    message:  message as Data  
    key:      key as Data  
    returns:  digest as Data  
    
    func hmac(hashName:String, message:Data, key:Data) -> Data? {
        let algos = ["SHA1":   (kCCHmacAlgSHA1,   CC_SHA1_DIGEST_LENGTH),
                     "MD5":    (kCCHmacAlgMD5,    CC_MD5_DIGEST_LENGTH),
                     "SHA224": (kCCHmacAlgSHA224, CC_SHA224_DIGEST_LENGTH),
                     "SHA256": (kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH),
                     "SHA384": (kCCHmacAlgSHA384, CC_SHA384_DIGEST_LENGTH),
                     "SHA512": (kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH)]
        guard let (hashAlgorithm, length) = algos[hashName]  else { return nil }
        var macData = Data(count: Int(length))
    
        macData.withUnsafeMutableBytes {macBytes in
            message.withUnsafeBytes {messageBytes in
                key.withUnsafeBytes {keyBytes in
                    CCHmac(CCHmacAlgorithm(hashAlgorithm),
                           keyBytes,     key.count,
                           messageBytes, message.count,
                           macBytes)
                }
            }
        }
        return macData
    

    }

    hashName: name of a hash function as String
    message:  message as String
    key:      key as String
    returns:  digest as Data
    
    func hmac(hashName:String, message:String, key:String) -> Data? {
        let messageData = message.data(using:.utf8)!
        let keyData = key.data(using:.utf8)!
        return hmac(hashName:hashName, message:messageData, key:keyData)
    }
    

    hashName: name of a hash function as String  
    message:  message as String  
    key:      key as Data  
    returns:  digest as Data  
    
    func hmac(hashName:String, message:String, key:Data) -> Data? {
        let messageData = message.data(using:.utf8)!
        return hmac(hashName:hashName, message:messageData, key:key)
    }
    

    // Examples

    let clearString = "clearData0123456"
    let keyString   = "keyData8901234562"
    let clearData   = clearString.data(using:.utf8)!
    let keyData     = keyString.data(using:.utf8)!
    print("clearString: \(clearString)")
    print("keyString:   \(keyString)")
    print("clearData: \(clearData as NSData)")
    print("keyData:   \(keyData as NSData)")
    
    let hmacData1 = hmac(hashName:"SHA1", message:clearData, key:keyData)
    print("hmacData1: \(hmacData1! as NSData)")
    
    let hmacData2 = hmac(hashName:"SHA1", message:clearString, key:keyString)
    print("hmacData2: \(hmacData2! as NSData)")
    
    let hmacData3 = hmac(hashName:"SHA1", message:clearString, key:keyData)
    print("hmacData3: \(hmacData3! as NSData)")
    

    Output:

    clearString: clearData0123456
    keyString:   keyData8901234562
    clearData: <636c6561 72446174 61303132 33343536>
    keyData:   <6b657944 61746138 39303132 33343536 32>
    
    hmacData1: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
    hmacData2: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
    hmacData3: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
    
    0 讨论(0)
  • 2021-01-14 17:02

    First of all it might be better to go straight for SHA512, SHA is notoriously easy to crack with GPU's, thus upping the memory scale a bit is not a bad idea.

    Second, using CommonCrypto it is actually extremely easy to generate HMAC's, this is the implementation that I use:

    static func hmac(_ secretKey: inout [UInt8], cipherText: inout [UInt8], algorithm: CommonCrypto.HMACAlgorithm = .sha512) -> [UInt8] {
            var mac = [UInt8](repeating: 0, count: 64)
            CCHmac(algorithm.value, &secretKey, secretKey.count, &cipherText, cipherText.count, &mac)
            return mac
        }
    

    Where the algorithm is defined as such:

    enum HMACAlgorithm {
            case sha512
    
            var value: UInt32 {
                switch(self) {
                case .sha512:
                    return UInt32(kCCHmacAlgSHA512)
                }
            }
        }
    

    My cipher text is cipherText+IV in this instance. When you are not using AES-GCM it seems suggested / recommended to HMAC IV+Cipher, but I cannot give you the technical details as to why.

    Converting Data or NSData to a byte array:

    var byteArray = data.withUnsafeBytes { [UInt8](UnsafeBufferPointer(start: $0, count: data.count) }
    

    The reason for using an array is a substantial performance increase over Data, I don't know what the core team is doing but Data performs worse than NSMutableData even.

    0 讨论(0)
提交回复
热议问题