CommonHMAC in Swift
Solution 1
You can do it in Swift. Just make sure you add #import <CommonCrypto/CommonHMAC.h>
to the bridging Objective-C bridging header.
Update: For Swift 4 see a much better solution using the Swift Package Manager here: https://github.com/jernejstrasner/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)
}
}
Solution 2
Try this for 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)
}
}
Don't forget add #import <CommonCrypto/CommonHMAC.h>
to Header.h
Solution 3
I wanted to keep things minimal. Avoid the added complexity of creating a generic class that could handle all the different digest types and instead just have a small method I could drop into a class if needed. I also prefer to avoid adding extensions to core classes.
Add the following include to your -Bridging-Header.h
file:
#import <CommonCrypto/CommonHMAC.h>
Then in the class that needs to call CCHmac()
add a private method:
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)
}
If I need a different CCHmacAlgorithm
I would just replace the two constants in that method with the appropriate ones. In my case I needed kCCHmacAlgSHA256
and CC_SHA256_DIGEST_LENGTH
.
Thanks to Jernej Strasner and others for the other answers, I just wanted something simpler for my case.
Solution 4
This is the full source of "How to import CommonCrypto in Swift project without Obj-c briging header", modified for Swift 3.0. The actual code work is of "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)
}
}
The following is example of usage.
// 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)")
I hope this could be a ready reference for some user like me. :)
Solution 5
Swift can figure out how to map to the Obj-C frameworks but not so much for direct C functions. Apple has provided some bindings for stuff like GCD and AudioToolbox, but not everything. It seems CommonCrypto does not have proper bindings yet.
For this case, I would recommend writing your own basic wrappers in Obj-C, then use these wrapper classes in Swift.
For example you could create an HMAC
class in 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;
}
}
Then in Swift:
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
}
}
Just remember to add #import "HMAC.h"
to your Swift bridging header as well as #import "<##Your-Project-Name##>-Swift.h"
to the Obj-C implementation (.m) file.
![Matt Donnelly](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Matt Donnelly
Updated on March 17, 2020Comments
-
Matt Donnelly over 4 years
I'm trying to create a HMAC SHA-1 hash of a string in Swift but can't figure out how to interact with the APIs as it doesn't seem to be importing the CommonCrypto framework. I've tried various different forms of "import CommonCrypto" and creating a bridging header file but none of it made a difference.
The odd thing is that if I create an Objective-C class, I'm able to interact with APIs without any problems, so this seems to be unique to Swift.
Also if anyone could tell me what the equivalent of
uint8_t digest[CC_SHA1_DIGEST_LENGTH]
is in Swift I'd be very grateful -
Mihai Fratu about 10 yearsHm... Can you explain how I would get the MD5 of a string using
calculate(algorithm:HMACAlgorithm, key:Byte[], data:Byte[]) -> Byte[]
in the end? I did create my wrapper class and did import it to Swift but I don't know how to get the MD5 hash out of that method. Thanks! -
Joseph Duffy about 10 yearsThis is yielding some odd results? Running the following:
var data = "tohash".dataUsingEncoding(NSASCIIStringEncoding).bytes let key = "testing2".dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false).bytes let computedHMAC = HMAC.calculateWithAlgorithm(HMACAlgorithm.SHA256, forKey: key, andData: data) let hashString = NSString(data: computedHMAC, encoding: NSASCIIStringEncoding) println(hashString)
yields«£#sübwôt5áW²¡Å^ø
-
Matt Donnelly about 10 yearsInteresting. Is this new in Beta 2? I think I tried it at the time, but maybe I'm wrong. Thanks for the response!
-
Jernej Strasner almost 10 years@MattDonnelly It might be. I have not tried this under Beta 1. I did successfully import other C level framework headers trough the bridging header in Beta 1 though.
-
Jernej Strasner almost 10 years@MihaiFratu for getting an MD5 hash of a string check out my answer at stackoverflow.com/questions/24123518/…
-
Mihai Fratu almost 10 yearsCool, thanks! In the end I used my old
Objective-C
category on NSString together with my Swift code. But your solution is nice too! -
Sjors Provoost almost 10 yearsAccording to Xcode you need to use the SystemConfiguration framework, not CommonCrypto. Once I figured out how to manually create a bridging header, this worked great.
-
almel almost 10 yearsYou're method of porting obj-c to swift is correct, but your objc-c HMAC logic is not as good as given here stackoverflow.com/questions/8458917/…. Trust me, I spent 5 hours trying to tweak your code to make it so that I could pass in strings and it did not work.
-
Nurbol almost 10 yearsShould it be ..< with the updated syntax (for i in 0..<digestLen) ?
-
Sjors Provoost almost 10 yearsAs of Xcode 6 Beta 4 the bridging header solution no longer works when you are creating your own framework. You need to use a module.map file instead. I created SHA256-Swift using this technique.
-
Jason G over 9 yearsso you put this in your swift file? and how do you use it?
-
Jernej Strasner over 9 years@naturalc You put this into any Swift file and then use it something like
"my string".hmac(.SHA512, key: "my key")
-
Mason G. Zhwiti over 9 yearsNot working as of Swift 1.2. Getting: Cannot invoke 'CCHmac' with an argument list of type '(CCHmacAlgorithm, UnsafePointer<Int8>, UInt, [CChar], UInt, UnsafeMutablePointer<CUnsignedChar>)'
-
Johan Kool over 9 years@MasonG.Zhwiti Change
strLen
andkeyLen
to be anInt
insteadUint
to make it work in Swift 1.2. -
Qadir Hussain about 9 years@JohanKool i changed to int but still its throwing the same error Cannot invoke 'CCHmac' with an argument list of type '(UInt32, UnsafePointer<Int8>, UInt, [CChar], (Int), UnsafeMutablePointer<CUnsignedChar>)'
-
Jernej Strasner about 9 years@QadirHussain See my updated code above. You can also find the whole code at github.com/jernejstrasner/SwiftCrypto/blob/master/SwiftCrypto/…
-
hdost over 8 yearsThis is not a problem, as the question specifically asks about using HMAC which inherently requires a key. en.wikipedia.org/wiki/Hash-based_message_authentication_code
-
David H over 7 yearsIt appears that the 'transform' pointer needs to be CFReleased: developer.apple.com/reference/security/…. Also for the life of me I cannot find kSecDigestHMACSHA1 defined anywhere (Swift 2.3, Xcode 8). But hoping to use this as a pure Swift solution!
-
rob mayoff over 7 yearsI don't think you need to manually release, because
SecDigestTransformCreate
doesn't return anUnmanaged
. ThekSecDigestHMACSHA1
constant is declared in the Security framework on macOS. Remember that my answer only applies to macOS, not iOS. -
David H over 7 yearsHah I saw then unsaw the OS X comment. Ah, I was just hoping .. Thanks!
-
ossamacpp over 7 yearsI guess better to extend Data (NSData) rather than extending String! Which will enable this implementation to be used for any data type can be converted to Data
-
vmeyer over 7 yearscStringUsingEncoding has been removed in Swift 3. Use
UnsafePointer<Int8>([Int8](string.utf8CString))
orUnsafePointer<Int8>([Int8](string.utf8))
instead -
Nabeel Khan about 7 yearswhere is header.h?
-
Nabeel Khan about 7 yearsthis gives error as: undeclared type CCHmacAlgorithm
-
Md Rais almost 3 yearsType 'UnsafeMutablePointer<CUnsignedChar>' (aka 'UnsafeMutablePointer<UInt8>') has no member 'alloc' Error in swift 5.