Genghis Jahn
Genghis Jahn

Reputation: 31

Equivalent Hashing in C# and Objective-C using HMAC256

I'm working with a partner and we're not able to get C# and Objective-C to produce the same hashes using what we think are the same tools in the respective languages. In C#, I'm doing this:

byte[] noncebytes=new byte[32];
//We seed the hash generator with a new 32 position array.  Each position is 0.
//In prod code this would be random, but for now it's all 0s.
HMACSHA256 hmac256 = new HMACSHA256(noncebytes);          
string plaintext = "hello";
string UTFString = Convert.ToBase64String(
    System.Text.Encoding.UTF8.GetBytes(plaintext));  
string HashString = Convert.ToBase64String(
    hmac256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(plaintext)));  //Convert that hash to a string.

This produces the following base64string hash:

Q1KybjP+DXaaiSKmuikAQQnwFojiasyebLNH5aWvxNo=

What is the equivalent Objective-C code to do this? We need the client and the server to be able to generate matching hashes for matching data.

Here is the Objective-C code we are currently using:

   ...

    NSData *zeroNumber = [self zeroDataWithBytes:32]; //empty byte array
    NSString *nonceTest = [zeroNumber base64String];  // using MF_Base64Additions.h here
    NSData *hashTest = [self hmacForKeyAndData:nonceTest withData:@"hello"]; //creating hash
    NSString *hashTestText = [hashTest base64String]; 
    NSLog(@"hello hash is %@",  hashTestText);

...

    //functions for zeroing out the byte.  I'm sure there's a better way

    - (NSData *)zeroDataWithBytes: (NSUInteger)length {
   NSMutableData *mutableData = [NSMutableData dataWithCapacity: length];
    for (unsigned int i = 0; i < length; i++) {
       NSInteger bits = 0;
      [mutableData appendBytes: (void *) &bits length: 1];
    } return mutableData;
}

//hash function

-(NSData *) hmacForKeyAndData:(NSString *)key withData:(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);
    return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}

Upvotes: 2

Views: 1127

Answers (2)

Genghis Jahn
Genghis Jahn

Reputation: 31

Note the second line of code in the objective-c portion of the question.

NSString *nonceTest = [zeroNumber base64String]; 

but it should be this:

NSString *nonceTest = [[NSString alloc] initWithData:zeroNumber encoding:NSASCIIStringEncoding];

It was a case of converting the string to base64 when we didn't need to for the hmac seeeding.

We now get: Q1KybjP+DXaaiSKmuikAQQnwFojiasyebLNH5aWvxNo= as the hash on both platforms.

Upvotes: 1

Mike D
Mike D

Reputation: 4946

UPDATE:

There is a pretty good project on GitHub that seems to accomplish everything you want, plus a lot more encryption related options; includes unit tests.


NSData *hmacForKeyAndData(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);
    return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}

(Source)

With the above, I think you will have import <CommonCrypto/CommonHMAC.h>. The next step for encoding to Base64:

+ (NSString *)Base64Encode:(NSData *)data
{
    //Point to start of the data and set buffer sizes
    int inLength = [data length];
    int outLength = ((((inLength * 4)/3)/4)*4) + (((inLength * 4)/3)%4 ? 4 : 0);
    const char *inputBuffer = [data bytes];
    char *outputBuffer = malloc(outLength);
    outputBuffer[outLength] = 0;

    //64 digit code
    static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    //start the count
    int cycle = 0;
    int inpos = 0;
    int outpos = 0;
    char temp;

    //Pad the last to bytes, the outbuffer must always be a multiple of 4
    outputBuffer[outLength-1] = '=';
    outputBuffer[outLength-2] = '=';

    /* http://en.wikipedia.org/wiki/Base64
     Text content   M           a           n
     ASCII          77          97          110
     8 Bit pattern  01001101    01100001    01101110

     6 Bit pattern  010011  010110  000101  101110
     Index          19      22      5       46
     Base64-encoded T       W       F       u
     */


    while (inpos < inLength){
        switch (cycle) {
            case 0:
                outputBuffer[outpos++] = Encode[(inputBuffer[inpos]&0xFC)>>2];
                cycle = 1;
                break;
            case 1:
                temp = (inputBuffer[inpos++]&0x03)<<4;
                outputBuffer[outpos] = Encode[temp];
                cycle = 2;
                break;
            case 2:
                outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xF0)>> 4];
                temp = (inputBuffer[inpos++]&0x0F)<<2;
                outputBuffer[outpos] = Encode[temp];
                cycle = 3;                  
                break;
            case 3:
                outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xC0)>>6];
                cycle = 4;
                break;
            case 4:
                outputBuffer[outpos++] = Encode[inputBuffer[inpos++]&0x3f];
                cycle = 0;
                break;                          
            default:
                cycle = 0;
                break;
        }
    }
    NSString *pictemp = [NSString stringWithUTF8String:outputBuffer];
    free(outputBuffer); 
    return pictemp;
}

Upvotes: 1

Related Questions