views:

1550

answers:

3

I have the following Objective-C function:

+(NSString *)stringToSha1:(NSString *)str{
    NSMutableData *dataToHash = [[NSMutableData alloc] init];
    [dataToHash appendData:[str dataUsingEncoding:NSUTF8StringEncoding]];

    unsigned char hashBytes[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1([dataToHash bytes], [dataToHash length], hashBytes);
    NSData *encodedData = [NSData dataWithBytes:hashBytes length:CC_SHA1_DIGEST_LENGTH];
    [dataToHash release];
    NSString *encodedStr = [NSString stringWithUTF8String:[encodedData bytes]];
    //NSString *encodedStr = [[NSString alloc]  initWithBytes:[encodedData bytes]
    //             length:[encodedData length] encoding: NSUTF8StringEncoding];
    NSLog(@"String is %@", encodedStr);

    return encodedStr;

}

What I'm trying to do is take an NSString and SHA1 encode it. That part seems to be working, I think where I am falling over is in how to convert the NSData object back to a legible string. If I use UTF8 encoding I get blank, if I say ASCII I get weird characters. What I really want is the hex string, but I have no idea how to get it. This is using the iPhone 3.0 SDK.

At the moment any String I pass in comes back out NULL.

+2  A: 

Short answer: turn on gcc warnings (-Wall).

Long answer:

NSMutableData *dataToHash = [[NSMutableData alloc] init];
[dataToHash appendData:[str dataUsingEncoding:NSUTF8StringEncoding]];

is broken: You try to use a C string where an NSData argument is expected. Use

NSMutableData *dataToHash = [str dataUsingEncoding:NSUTF8StringEncoding];

instead.

The rest of the method takes the SHA1 buffer and tries interpret this data as an UTF-8 C string, which might crash or give any unexpected result. First, the buffer is not a UTF-8 string. Secondly, it's not null terminated.

What you want is to convert the SHA1 to a base 64 or similar string. Here's a nice post about how to do that.

Nikolai Ruhe
thanks, I'm new to most of this C business, but it looks fairly easy to follow
rustyshelf
A: 

This is what I ended up with, the next step would be to convert it to be a Category of NSString instead of a static method in a helper class:

+(NSString *)stringToSha1:(NSString *)str{
    const char *s = [str cStringUsingEncoding:NSASCIIStringEncoding];
    NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];

    // This is the destination
    uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0};
    // This one function does an unkeyed SHA1 hash of your hash data
    CC_SHA1(keyData.bytes, keyData.length, digest);

    // Now convert to NSData structure to make it usable again
    NSData *out = [NSData dataWithBytes:digest length:CC_SHA1_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:@""];

    NSLog(@"Hash is %@ for string %@", hash, str);

    return hash;
}
rustyshelf
This can only create hashs for pure ASCII strings.
Nikolai Ruhe
Thanks for the tip, but that's all I need.
rustyshelf
+1  A: 

My version of SHA1 function (simplier):

- (NSString *)sha1:(NSString *)str {
const char *cStr = [str UTF8String];
unsigned char result[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(cStr, strlen(cStr), result);
NSString *s = [NSString  stringWithFormat:
      @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
      result[0], result[1], result[2], result[3], result[4],
      result[5], result[6], result[7],
      result[8], result[9], result[10], result[11], result[12],
      result[13], result[14], result[15],
      result[16], result[17], result[18], result[19]
      ];

return [s lowercaseString];

}

And MD5:

- (NSString *)md5:(NSString *)str {
const char *cStr = [str UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(cStr, strlen(cStr), result);
NSString *s = [NSString  stringWithFormat:
      @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
      result[0], result[1], result[2], result[3], result[4],
      result[5], result[6], result[7],
      result[8], result[9], result[10], result[11], result[12],
      result[13], result[14], result[15]
      ];

return [s lowercaseString];

}

glycol