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 h
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
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=
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)
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=
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
}
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