Skip to content

Instantly share code, notes, and snippets.

@MihaelIsaev
Last active December 4, 2019 04:51
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save MihaelIsaev/f913d84b918d2b2c067d to your computer and use it in GitHub Desktop.
Save MihaelIsaev/f913d84b918d2b2c067d to your computer and use it in GitHub Desktop.
Easy to use Swift implementation of CommonCrypto HMAC. You can easily hash your String to: md5, sha1, sha224, sha256, sha384, sha512 with pure Swift.
//
// 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.
//
// ***********************************************************
//
import Foundation
import CommonCrypto
extension String {
var md5: String {
return HMAC.hash(self, algo: HMACAlgo.MD5)
}
var sha1: String {
return HMAC.hash(self, algo: HMACAlgo.SHA1)
}
var sha224: String {
return HMAC.hash(self, algo: HMACAlgo.SHA224)
}
var sha256: String {
return HMAC.hash(self, algo: HMACAlgo.SHA256)
}
var sha384: String {
return HMAC.hash(self, algo: HMACAlgo.SHA384)
}
var sha512: String {
return HMAC.hash(self, algo: HMACAlgo.SHA512)
}
}
public struct HMAC {
static func hash(inp: String, algo: HMACAlgo) -> String {
if let stringData = inp.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
return hexStringFromData(digest(stringData, algo: algo))
}
return ""
}
private static func digest(input : NSData, algo: HMACAlgo) -> NSData {
let digestLength = algo.digestLength()
var hash = [UInt8](count: digestLength, repeatedValue: 0)
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](count: input.length, repeatedValue: 0)
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)
}
}
@thbwd
Copy link

thbwd commented Sep 12, 2015

This is not pure Swift. You are calling C and Foundation APIs.

@kaishin
Copy link

kaishin commented Mar 8, 2016

@idmean yes, but the API is pure Swift at least.

@helje5
Copy link

helje5 commented Jun 22, 2016

Please adjust your tagline, this is not pure Swift.

@kutschenator
Copy link

this is not hmac but just the hashing functions CCHmac is the correct C function to call

@inigo333
Copy link

@commscheck
Copy link

@nabtron
Copy link

nabtron commented May 7, 2018

Full tutorial with example project in xcode 9 and swift 4: https://nabtron.com/commoncrypto-hmac-swift/

@adamwaite
Copy link

Swift 4.2:

import Foundation
import CommonCrypto

extension String {
    
    var md5: String {
        return HMAC.hash(self, algo: .MD5)
    }
    
    var sha1: String {
        return HMAC.hash(self, algo: .SHA1)
    }
    
    var sha224: String {
        return HMAC.hash(self, algo: .SHA224)
    }
    
    var sha256: String {
        return HMAC.hash(self, algo: .SHA256)
    }
    
    var sha384: String {
        return HMAC.hash(self, algo: .SHA384)
    }
    
    var sha512: String {
        return HMAC.hash(self, algo: .SHA512)
    }
}

private struct HMAC {
    
    static func hash(_ inp: String, algo: HMACAlgo) -> String {
       
        if let stringData = inp.data(using: .utf8, allowLossyConversion: false) {
            
            return hexStringFromData(digest(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)
        case .SHA1: CC_SHA1(input.bytes, UInt32(input.length), &hash)
        case .SHA224: CC_SHA224(input.bytes, UInt32(input.length), &hash)
        case .SHA256: CC_SHA256(input.bytes, UInt32(input.length), &hash)
        case .SHA384: CC_SHA384(input.bytes, UInt32(input.length), &hash)
        case .SHA512: CC_SHA512(input.bytes, UInt32(input.length), &hash)
        }
        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
    }
}

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment