Search code examples
objective-cmacosshahmacnsstringencoding

NSString from NSData always null


I would like to sign a request with HMAC SHA512, but I seem to mess up encoding and decoding from and to NSData and NSString. I desperately tried to figure out what is wrong, but I just don't seem to get it right.

PSEUDOCODE:

function hmac_512(msg, sec) {
    sec = Base64Decode(sec);
    result = hmac(msg, sec, sha512);
    return Base64Encode(result);
}


secret = "7pgj8Dm6";
message = "Test\0Message";

result = hmac_512(message, secret);
if (result == "69H45OZkKcmR9LOszbajUUPGkGT8IqasGPAWqW/1stGC2Mex2qhIB6aDbuoy7eGfMsaZiU8Y0lO3mQxlsWNPrw==")
    print("Success!");
else
    printf("Error: %s", result);



My implementation:

+(void)doSomeMagic{

    NSString *message = @"Test\0Message";
    NSString *signedRequest = [self signRequestForParameterString:message];

    //checking against CORRECT (from JAVA equivalent) signed request 
    if ([signedRequest isEqualToString:@"69H45OZkKcmR9LOszbajUUPGkGT8IqasGPAWqW/1stGC2Mex2qhIB6aDbuoy7eGfMsaZiU8Y0lO3mQxlsWNPrw==" ])
        NSLog(@"Success!");
    else
        NSLog(@"Error!");
}

Here is the signing method:

+(NSString *)signRequestForParameterString:(NSString*)paramStr{

    NSString *secret = @"7pgj8Dm6";

    // secret is base64 encoded, so I decode it 
    NSData *decodedSecret = [secret base64DecodedData];
    NSString *decodedSecretString = [NSString stringWithUTF8String:[decodedSecret bytes]];

    NSData *data = [paramStr dataUsingEncoding:NSUTF8StringEncoding];
    NSString *dataString = [NSString stringWithUTF8String:[data bytes]];


    return [self generateHMACSHA512Hash:decodedSecretString data:dataString];

}

Here is the hashing function:

+(NSString *)generateHMACSHA512Hash:(NSString *)key data:(NSString *)data{


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

    unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH];

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

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

    NSString *hash = [HMAC base64EncodedString];

    return hash;

} 

I am pretty sure it is due to the encoding of the strings (decodedSecretString and dataString). decodedSecretString (decoded base64) after decoding is encoded in ASCII. However, when I call the hashing method, I encode it in ascii again, which will result in a null error. Everything is confusing me right now.


Solution

  • Your secret doesn't decode to a valid UTF-8 string, and Java allows NUL bytes in strings, but when you're converting "Test\0Message" to a C string and using strlen, its length is 4.

    Something like this should work:

    +(NSString *)signRequestForParameterString:(NSString*)paramStr{
        NSString *secret = @"7pgj8Dm6";
        NSData *data = [paramStr dataUsingEncoding:NSUTF8StringEncoding];
        return [self generateHMACSHA512Hash:[secret base64DecodedData] data:data];
    }
    
    +(NSString *)generateHMACSHA512Hash:(NSData *)key data:(NSData *)data{
        unsigned char cHMAC[CC_SHA512_DIGEST_LENGTH];
        CCHmac(kCCHmacAlgSHA512, key.bytes, key.length, data.bytes, data.length, cHMAC);
        NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
        return [HMAC base64EncodedString];
    }