63

I need to generate HMAC-SHA1 in Objective C. But i didnt find anything that works. I tried with CommonCrypto, using CCHMAC, but didnt works. I need to generate a hmac and after generate HOTP number.

Somebody have any example code in Objective C or C?

Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
Helena
  • 741
  • 1
  • 6
  • 5
  • I don´t understand why you are using the base64Encoding, if all we want is to have a string of the generated hash. Can you explain, because, in the end we get a base64 encoded hmac-sha256, instead of a hmac-sha256... – bruno Oct 10 '11 at 18:17
  • @bruno in case you didn't notice, your answer has been deleted and made into a comment. If you have more to post, post it as new answer. – Shadow The GPT Wizard Oct 11 '11 at 09:37

8 Answers8

74

Here's how you generate an HMAC using SHA-256:

NSString *key;
NSString *data;

const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];

unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];

CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC
                                      length:sizeof(cHMAC)];

NSString *hash = [HMAC base64Encoding];

I'm not aware of an HOTP library, but the algorithm was quite simple, if I recall correctly.

Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
  • 1
    base64Encoding have in object c??I tried compile it, but i get an error this line. [HMAC base64Encoding]; – Helena Apr 16 '09 at 18:59
  • 1
    @Helena: oops, base64Encoding is from a custom NSData protocol. =) I'm glad the rest of the code worked, though. – Can Berk Güder Apr 16 '09 at 23:20
  • Note that you can find a base 64 encoding category method on NSData in GHKit. See: https://github.com/gabriel/gh-kit. The method is '-gh_base64' and returns an NSString. Use this in place of the 'base64Encoding' category method call in the above example if you don't have the same category as the author. – idStar Aug 02 '12 at 15:33
  • I actually liked the base 64 "addition" because I can use this with HMAC-SHA1 for OAuth. – spstanley Oct 12 '12 at 14:20
  • I've defined *key and *data with two sample strings. Using [HMAC gh_base64] I get this exception because returned value is nil: 'NSInvalidArgumentException', reason: '-[NSConcreteData gh_base64]: unrecognized selector sent to instance 0xad55310'. Why is it nil?? – brainondev Nov 08 '12 at 21:43
  • 2
    Note this is Sha256 not Sha1 see Zivic Sanel's answer – Colin Mar 06 '13 at 13:52
  • CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC); <-- on this line i am getting EXC_BAD_ACESS . Any idea ? – Muhammad Saad Ansari Nov 10 '13 at 08:11
  • Is there a reason to use ASCII encoding? – André Fratelli Jun 14 '15 at 19:43
  • Exactly 64 upvotes for base64Encoding – Nikhil Mathew Feb 26 '16 at 05:05
39

here is how you can generate HMAC-SHA1 base64.

You need to add Base64.h and Base64.m to your project. You can get it from here.

If you use ARC, it will show some errors in Base64.m. Find the lines who are similar like this

return [[[self alloc] initWithBase64String:base64String] autorelease];

what you need is to delete the autorelease section. The final result should look like:

return [[self alloc] initWithBase64String:base64String];

Now in your general project import "Base64.h" and the following code

#import "Base64.h"
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>

- (NSString *)hmacsha1:(NSString *)data secret:(NSString *)key {

    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];

    unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];

    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

    NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];

    NSString *hash = [HMAC base64String];

    return hash;
}

With

NSLog(@"Hash: %@", hash);  

you will get something similar to this:

ghVEjPvxwLN1lBi0Jh46VpIchOc=

 

Zsivics Sanel
  • 1,047
  • 1
  • 15
  • 17
21

This is the complete solution which works without any extra libraries or hacks:

+(NSString *)hmac:(NSString *)plainText withKey:(NSString *)key
{
    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    const char *cData = [plainText cStringUsingEncoding:NSASCIIStringEncoding];

    unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];

    CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

    NSData *HMACData = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];

    const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
    NSString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];

    for (int i = 0; i < HMACData.length; ++i)
        HMAC = [HMAC stringByAppendingFormat:@"%02lx", (unsigned long)buffer[i]];

    return HMAC;
}

You don't have to include any third-party base64 library as it is already encoded.

Maciej Ciemięga
  • 10,125
  • 1
  • 41
  • 48
codeplasma
  • 515
  • 3
  • 6
  • Are you sure this is already Base 64 encoded? What makes it base 64 encoded? Sorry to bother you, but I am troubleshooting my difficulty in generating an oauth signature. I tried to use your algorithm but I realize I do not know/see where it is Base 64 encoded. – helloB Feb 04 '16 at 17:13
  • 2
    ⚠️ Warning to the copy-pasters: **be careful with the encoding type!!** I was encoding emoji characters and switched `NSASCIIStringEncoding` to `NSUTF16StringEncoding` which introduced null terminators after each character into the returned c array, leading `strlen` to think the array's length was 1, which caused the HMAC to be based purely on the first character. – Warpling Mar 24 '17 at 03:09
  • What's CCHmac ? – Harish Pathak Sep 29 '17 at 09:28
  • this answer is using sha256 where as the question is asking for sha1. Surprisingly no one has pointed that out in the past 6 years? – dollardime Sep 22 '20 at 18:20
9

This works without using custom protocols, using some code from http://cocoawithlove.com/2009/07/hashvalue-object-for-holding-md5-and.html

HashSHA256.h

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>

@interface HashSHA256 : NSObject {


}

 - (NSString *) hashedValue :(NSString *) key andData: (NSString *) data ; 

@end

HashSHA256.m

#import "HashSHA256.h"

#import <CommonCrypto/CommonHMAC.h>


@implementation HashSHA256


- (NSString *) hashedValue :(NSString *) key andData: (NSString *) data {


    const char *cKey  = [key cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
    unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

    NSString *hash;

    NSMutableString* output = [NSMutableString   stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];

    for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", cHMAC[i]];
    hash = output;
    return hash;

}

@end

Usage:

- (NSString *) encodePassword: (NSString *) myPassword {
    HashSHA256 * hashSHA256 = [[HashSHA256 alloc] init];   
    NSString * result = [hashSHA256 hashedValue:mySecretSalt andData:myPassword];       
    return result;       
}
primulaveris
  • 966
  • 14
  • 20
  • 1
    You shouldn’t just use the string as a key. Instead you should derive it and make a proper key that is longer. PBKDF2 would be certainly a start. – Rafael Bugajewski May 21 '12 at 08:06
2

This is how yo do it without external files returning an hex string:

-(NSString *)hmac:(NSString *)plaintext withKey:(NSString *)key
{
    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    const char *cData = [plaintext cStringUsingEncoding:NSASCIIStringEncoding];
    unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
    const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
    NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
    for (int i = 0; i < HMACData.length; ++i){
        [HMAC appendFormat:@"%02x", buffer[i]];
     }
   return HMAC;
}

It was tested in xCode 5 with iOS 7 and works fine!

Neco
  • 36
  • 3
  • DO NOT USE `NSASCIIStringEncoding`. Use `NSUTF8StringEncoding` if you wish to support characters in non English languages. – Alex Zavatone Feb 07 '20 at 16:35
  • this should be the accepted answer. the other popular answers are either returning a base64 encoded value or using sha256 which the question doesn't ask for. – dollardime Sep 22 '20 at 18:22
1

Out of interest, why do you create (unsigned char cHMAC) and then convert into (NSData) and then convert it into (NSMutableString) and then convert finally into (HexString)?

You could do this in a quicker way by cutting the middleman (i.e. without NSData and NSMutableString altogether, quicker and better performance), also changing (unsigned char) into (uint8_t []), after all they are all hex-arrays anyway!, below:

-(NSString *)hmac:(NSString *)plaintext withKey:(NSString *)key
{
const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [plaintext cStringUsingEncoding:NSASCIIStringEncoding];

uint8_t cHMAC[CC_SHA1_DIGEST_LENGTH];

CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

NSString *Hash1 = @"";
for (int i=0; i< CC_SHA1_DIGEST_LENGTH; i++)
{
    Hash1 = [Hash1 stringByAppendingString:[NSString stringWithFormat:@"%02X", cHMAC[i]]];
}
return Hash1;
}

I hope this helps,

Regards

Heider Sati

Heider Sati
  • 2,476
  • 26
  • 28
1

I spend a whole day, trying to convert the generated hash (bytes) into readable data. I used the base64 encoded solution from the answer above and it didn´t work at all for me (b.t.w. you need and an external .h to be able to use the base64 encoding, which I had).

So what I did was this (which works perfectly without an external .h):

CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

// Now convert to NSData structure to make it usable again
NSData *out = [NSData dataWithBytes:cHMAC length:CC_SHA256_DIGEST_LENGTH];

// description converts to hex but puts <> around it and spaces every 4 bytes
NSString *hash = [out description];
hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
// hash is now a string with just the 40char hash value in it
NSLog(@"%@",hash);
Rafael Bugajewski
  • 1,702
  • 3
  • 22
  • 37
bruno
  • 2,154
  • 5
  • 39
  • 64
0

Have you seen Jens Alfke's new MyCrypto classes?

He has some sample code on his blog.

Ford
  • 1,485
  • 3
  • 14
  • 20