I am relatively new to Swift and i'm stuck encrypting using HMAC and SHA1. I Found the following answer https://stackoverflow.com/a/24411522/4188344 but i can't work out how to implement this properly. Any help would be amazing.
- 
                    1I have used this exact answer and it worked for me. Can you describe the issues you are having? – maksimov Nov 17 '14 at 10:46
- 
                    maybe copying and pasting the code and using the `–hmac(algorithm:key:)` method on your `String` objects...? – holex Nov 17 '14 at 10:46
- 
                    I managed to get a solution for this working in Objective-C, i think i'm missing the equivalent of : @interface HashSHA1 : NSObject - (NSString *) hashedValue :(NSString *) key andData: (NSString *) data ; – David Wood Nov 17 '14 at 11:16
- 
                    1HMAC and SHA1 are not encryption. Common Crypto can be used from Swift and provide the primitives you need. – zaph Nov 17 '14 at 13:50
- 
                    The answer you link to is in Swift and @maksimov has also provided an answer. Add to the question your code that is using this and someone can probably find the problem. – zaph Nov 17 '14 at 15:34
- 
                    ok, sorry for being a noob on this. I am trying to use the answer linked in my original question like this: `let hmacResult:String = "StringToHash".hmac(HMACAlgorithm.SHA1, key: "MyKey")` The output i'm getting is "45e3e84c4d20d3ca1a700d47e5742a5ab04882a8" but i'm expecting "ztmovDWU7kQPGSwmB/RaughkdAo=". I'm sure I'm doing something really basic wrong. Thanks so much for everyone's input. – David Wood Nov 18 '14 at 01:39
- 
                    For laravel encryption you can use pod 'LaraCrypt' and find mac function in it. swift3 – reza_khalafi May 25 '17 at 05:57
9 Answers
Problem solved! First off i wasn't using the string function properly... I ended up with this:
    let hmacResult:String = "myStringToHMAC".hmac(HMACAlgorithm.SHA1, key: "myKey")
Then I had forgotten I needed to base64 encode the hmac result. So i modified the string function linked in my question to...
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)
    }
}
This is giving me the correct result of
lGCtbW+DNHFraNoxPGK3trgM/98=
 
    
    - 1,319
- 2
- 12
- 15
- 
                    
- 
                    var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0) has error in new swift – reza_khalafi May 06 '17 at 08:29
- 
                    Are you sure you are getting the correct result? You should not be getting it, since you pass your key & data with an extra `\0` at the end, because you are using cStrings (null terminated) – Daniel Jun 22 '18 at 07:26
- 
                    Not getting the same results. I'm getting 85afQardtIk0rRWRlQ10qrL/STc= – Warpzit Sep 24 '18 at 10:58
Here is @David Wood's solution updated for 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")
 
    
    - 17,523
- 6
- 79
- 92
- 
                    I'm getting an error 'Argument count must be precede by repeating'. This error is reported in line number 3 of hmac function. I'm using Xcode 8.2.1 and Swift 3. – Jayprakash Dubey Mar 15 '17 at 04:56
- 
                    @JayprakashDubey sounds like you have a typo? I'm running this exact code in a couple of projects. The error you mention sounds like it's on this intializer, which is correct in the above code: https://developer.apple.com/reference/swift/array/1641692-init# – Christopher Pickslay Mar 16 '17 at 17:53
Here is how to create a Swift 4 extension:
Bridging headers file
#import <CommonCrypto/CommonCrypto.h>
Code
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()
    }
}
Example
let result = "test".hmac(key: "test")
Result
0c94515c15e5095b8a87a50ba0df3bf38ed05fe6
- 
                    @user3745635 You need to import Apple security headers. See the updated post. – Blago Sep 10 '18 at 12:09
- 
                    @sundance downvoted because of a buggy code, with russian symbols in data gives different results in comparison with https://stackoverflow.com/a/27032056/887549 – bezigon Mar 22 '21 at 12:32
If you want the same result in hexadecimal format, you can use the following extension:
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
    }
}
 
    
    - 55,258
- 23
- 97
- 137
 
    
    - 967
- 1
- 14
- 33
I have used this module which I added to my project as a framework:
https://github.com/CryptoCoinSwift/SHA256-Swift
And I have also added the following String extension to 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)
    }
}
This way producing a base64-encoded signature from a String can be done like this:
let signature: String = "\(payload)".sha256(secretKey)
 
    
    - 5,792
- 1
- 30
- 38
- 
                    1Why an extension when a simple method will do? Does this method really belong as part of the String class? – zaph Nov 17 '14 at 13:54
- 
                    2Well, there should be a better reason than "Why not". If there is not a clear reason and advantage to make it an extension then don't. The question that needs to be answered: "Is this a natural and expected part of the `String` class?" Would I expect to find all things crypto in the String class? – zaph Nov 17 '14 at 14:58
- 
                    4Sure, you're right. In my case, it was a little prototype project and me just starting with Swift I was like "Oh look, I can extend String class and make this shortcut, wow!". Nobody was hurt during this experiment. :-) – maksimov Nov 17 '14 at 15:18
I have checked the above answers and found it so lengthy.
Solution: I got third party: IDZSwiftCommonCrypto
Use pod: pod 'IDZSwiftCommonCrypto'
and use the following function to achieve desired output:
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
    }
}
For check the result use following website:
https://hash.online-convert.com/sha1-generator
Tested in Swift 4.0
 
    
    - 863
- 10
- 26
Using raw bytes for key and message and not encoding to 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
     }
 
    
    - 399
- 3
- 6
In Swift 4 You need library CommonCrypto https://forums.developer.apple.com/thread/46477
#import <CommonCrypto/CommonCrypto.h>
And you can create extension with 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))
    }
}
Usege:
print("HMAC_SHA256:".hmac(key: "MyKey"))
Result:
6GM2evJeNZYdP3OjPcKmg8TDzILSQAjy4NGhCHnBH5M=
 
    
    - 57
- 3
- 
                    
- 
                    @Sergei downvoted, see my comment in https://stackoverflow.com/a/48406753/887549 – bezigon Mar 22 '21 at 12:33
Looks like you can do this with Apple's CryptoKit framework now:
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()
    }
}
 
    
    - 12,831
- 4
- 56
- 53
 
     
    