Generate a Hmac Swift Sdk8.3 Using Cchmac()

Generate a HMAC swift sdk8.3 using CCHmac()

The problem is that strlen returns a UInt, while CCHmac’s length arguments are Ints.

While you could do some coercion, you may as well just use the count property of the two arrays rather than calling strlen.

func generateHMAC(key: String, data: String) -> String {

var result: [CUnsignedChar]
if let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding),
cData = data.cStringUsingEncoding(NSUTF8StringEncoding)
{
let algo = CCHmacAlgorithm(kCCHmacAlgSHA512)
result = Array(count: Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0)

CCHmac(algo, cKey, cKey.count-1, cData, cData.count-1, &result)
}
else {
// as @MartinR points out, this is in theory impossible
// but personally, I prefer doing this to using `!`
fatalError("Nil returned when processing input strings as UTF8")
}

let hash = NSMutableString()
for val in result {
hash.appendFormat("%02hhx", val)
}

return hash as String
}

Generating SHA256 in iOS

The key here is you need to convert your secret, which is a hex string, into NSData. In other words, NSData byte stream would "look" like the secret.

This should do what you want:

    // Hex string to NSData conversion from here http://stackoverflow.com/questions/7317860/converting-hex-nsstring-to-nsdata
NSString *secret = @"71DD0F73AFFBB47825FF9864DDE95F3B";
NSData *dataIn = [@"Amount=50&BillerID=59&ChannelID=2&Context=34|check|test&ReturnURL=https://uat.myfatoora.com/ReceiptPOC.aspx&TxnRefNum=000000000020003&UserName=DCS" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];

secret = [secret stringByReplacingOccurrencesOfString:@" " withString:@""];
NSMutableData *secretData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [secret length]/2; i++) {
byte_chars[0] = [secret characterAtIndex:i*2];
byte_chars[1] = [secret characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[secretData appendBytes:&whole_byte length:1];
}

CCHmac(kCCHmacAlgSHA256, secretData.bytes, secretData.length, dataIn.bytes, dataIn.length, macOut.mutableBytes);

NSMutableString *stringOut = [NSMutableString stringWithCapacity:macOut.length];
const unsigned char *macOutBytes = macOut.bytes;

for (NSInteger i=0; i<macOut.length; ++i) {
[stringOut appendFormat:@"%02x", macOutBytes[i]];
}

NSLog(@"dataIn: %@", dataIn);
NSLog(@"macOut: %@", macOut);
NSLog(@"stringOut: %@", stringOut);

Output:

2016-09-27 20:18:54.181 JKS[27562:5321334] dataIn: <416d6f75 6e743d35 30264269 6c6c6572 49443d35 39264368 616e6e65 6c49443d 3226436f 6e746578 743d3334 7c636865 636b7c74 65737426 52657475 726e5552 4c3d6874 7470733a 2f2f7561 742e6d79 6661746f 6f72612e 636f6d2f 52656365 69707450 4f432e61 73707826 54786e52 65664e75 6d3d3030 30303030 30303030 32303030 33265573 65724e61 6d653d44 4353>

2016-09-27 20:18:54.181 JKS[27562:5321334] macOut: <94a20ca3 9c8487c7 763823ec 9c918d9e 38ae83cb 741439f6 d129bcde f9edba73>

2016-09-27 20:18:54.181 JKS[27562:5321334] stringOut: 94a20ca39c8487c7763823ec9c918d9e38ae83cb741439f6d129bcdef9edba73

Updated with Swift (code should be cleaned up)

// http://stackoverflow.com/questions/29799361/generate-a-hmac-swift-sdk8-3-using-cchmac
func generateHMAC(key: String, data: String) -> String {
let keyData = key.dataFromHexadecimalString()! as NSData
let dataIn = data.data(using: .utf8)! as NSData
var result: [CUnsignedChar]
result = Array(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyData.bytes, keyData.length, dataIn.bytes, dataIn.length, &result)

let hash = NSMutableString()
for val in result {
hash.appendFormat("%02hhx", val)
}

return hash as String
}

You can use this extension to convert the hex string to Data

// Modified slightly http://stackoverflow.com/questions/26501276/converting-hex-string-to-nsdata-in-swift
extension String {

func dataFromHexadecimalString() -> Data? {
var data = Data(capacity: characters.count / 2)

let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)
data.append(&num!, count: 1)
}

return data
}
}

And to use do something like:

    let secret = "71DD0F73AFFBB47825FF9864DDE95F3B"
let value = "Amount=50&BillerID=59&ChannelID=2&Context=34|check|test&ReturnURL=https://uat.myfatoora.com/ReceiptPOC.aspx&TxnRefNum=000000000020003&UserName=DCS"

print("\(generateHMAC(key: secret, data: value))")

Your output should be 94a20ca39c8487c7763823ec9c918d9e38ae83cb741439f6d129bcdef9edba73

You will need #import <CommonCrypto/CommonCrypto.h> in your bridging header.

Cannot invoke 'CCHmac' with an argument list of type '(UInt32, UnsafePointerInt8, UInt, [CChar], Int, UnsafeMutablePointerCUnsignedChar)

I solved it just cast the strlen(cKey) to int like Int(strlen(cKey))

whole line look like is

CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey, Int(strlen(cKey)), str!, strLen, result)

How to hash NSString with SHA1 in Swift?

Your Objective-C code (using a NSString category) can be directly translated to Swift
(using a String extension).

First you have to create a "bridging header" and add

#import <CommonCrypto/CommonCrypto.h>

Then:

extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in digest {
output.appendFormat("%02x", byte)
}
return output as String
}
}

println("Hello World".sha1())

This can be written slightly shorter and Swifter as

extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = map(digest) { String(format: "%02hhx", $0) }
return "".join(hexBytes)
}
}

Update for Swift 2:

extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")
}
}

To return a Base-64 encoded string instead of a hex encoded string,
just replace

        let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")

with

        return NSData(bytes: digest, length: digest.count).base64EncodedStringWithOptions([])

Update for Swift 3:

extension String {
func sha1() -> String {
let data = self.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

To return a Base-64 encoded string instead of a hex encoded string,
just replace

        let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()

by

        return Data(bytes: digest).base64EncodedString()

Update for Swift 4:

The bridging header file is no longer needed, one can import CommonCrypto instead:

import CommonCrypto

extension String {
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

Update for Swift 5:

The Data.withUnsafeBytes() method now calls the closure with an UnsafeRawBufferPointer to, and baseAddress is used to pass the initial address to the C function:

import CommonCrypto

extension String {
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

sysctlbyname failing in swift 1.2

The solution is there in the comment in your code: Size is now Int rather than Uint in 1.2, so this compiles:

var deviceModelIdentifier: String {
var size : Int = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](count: size, repeatedValue: 0)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
return String.fromCString(machine)!
}

(you can also write var size : size_t = 0 if you prefer)

Error message hints at this when you wade through the unsafe pointer boiler plate:

note: expected an argument list of type '(UnsafePointer< Int8 >,
UnsafeMutablePointer< Void >, UnsafeMutablePointer<Int>,
UnsafeMutablePointer< Void >, Int)'



Related Topics



Leave a reply



Submit