43

How can I create a SHA1 from a NSString.

Let's say the NSString is set up as:

NSString *message = @"Message";

I can use PHP to create a SHA1 hash with sha($message). But unfortunately it doesn't work like that within Objective-C.

hypercrypt
  • 15,389
  • 6
  • 48
  • 59
Alex van Rijs
  • 803
  • 5
  • 17
  • 39
  • 1
    You could look at [`CC_SHA1`](http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/CC_SHA1.3cc.html). Also see [here](http://stackoverflow.com/questions/1353771/trying-to-write-nsstring-sha1-function-but-its-returning-null) – Alex Sep 27 '11 at 14:01
  • Definitely post it as an answer! – Wevah Sep 27 '11 at 15:05
  • I like hypercrypt's answer so much I packaged it into a little git repo. Check out the NSString category [on Github.](https://github.com/atreat/NSString-Sha1) Also feel free to add to it with any other good NSString Crypto – atreat Mar 29 '12 at 03:36

7 Answers7

93

I have this in a category on NSString (available at https://github.com/hypercrypt/NSString-Hashes):

#import <CommonCrypto/CommonDigest.h>

...

- (NSString *)sha1
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];

    CC_SHA1(data.bytes, (CC_LONG)data.length, digest);

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

    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
    {
        [output appendFormat:@"%02x", digest[i]];
    }

    return output;
}

Starting with Xcode 10.0, you should use import CommonCrypto instead since it is now natively available in Swift! If you have recently migrated to Xcode 10.0 and use the old approach, this can be your cue to make the change:

Command CompileSwift failed with a nonzero exit code

Omid Ariyan
  • 1,164
  • 13
  • 19
hypercrypt
  • 15,389
  • 6
  • 48
  • 59
  • 3
    Doesn't that require you to import ? – Bill Burgess Sep 27 '11 at 15:47
  • 12
    Xcode 4 complains about the line: CC_SHA1(data.bytes, data.length, digest); Replace with: CC_SHA1(data.bytes, (CC_LONG) data.length, digest); – A. R. Younce Mar 05 '12 at 03:49
  • Why do you init the mutableString with CC_SHA1_DIGEST_LENGTH*2 and not just do [NSMutableString string] ? – Oliver Jul 09 '12 at 03:14
  • 2
    It creates a mutable string with enough capacity to hold the whole output string, that way the string's storage doesn't need to be expanded during the loop. It is usually best to use the `*WithCapacity:` methods for the `NSMutable*` classes if you know the size you'll need before a loop (or an upper bound) as the correct memory size can then be allocated at start. – hypercrypt Jul 09 '12 at 08:30
15

I quite like hypercrypt's answer, but I've been encouraged to post my comment.

You could look at CC_SHA1, or this related SO question.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Alex
  • 9,313
  • 1
  • 39
  • 44
13
- (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;
}
virata
  • 1,882
  • 15
  • 22
4

It took me a while to port @hypercrypt solution to Swift so I decided to share it with others that might have the same problem.

One important thing to note is that you need CommonCrypto library, but that library does not have Swift module. The easiest workaround is to import it in your bridging header:

#import <CommonCrypto/CommonCrypto.h>

Once imported there, you do not need anything else. Just use String extension provided:

extension String
{
    func sha1() -> String
    {
        var selfAsSha1 = ""

        if 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)

            for index in 0..<CC_SHA1_DIGEST_LENGTH
            {
                selfAsSha1 += String(format: "%02x", digest[Int(index)])
            }
        }

        return selfAsSha1
    }
}

Notice that my solution does not take effect of reserving capacity what NSMutableString has in original post. However I doubt anyone will see the difference :)

hris.to
  • 6,235
  • 3
  • 46
  • 55
  • Very nice but why an extension? – zaph Aug 12 '15 at 14:56
  • Well, mostly because the original answer used Category, so the common association in my head was Extension :) I think it's easy enough and just adds needed functionality to String. I'm open for better solutions :) – hris.to Aug 13 '15 at 05:48
3

try this:

#import <CommonCrypto/CommonDigest.h>

-(NSData *) selector
{
    unsigned char hashBytes[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1([dataToHash bytes], [dataToHash length], hashBytes);
    NSData *data = [[NSData alloc] initWithBytes:hashBytes length:CC_SHA1_DIGEST_LENGTH];
}
liclac
  • 412
  • 3
  • 9
Saikrishna Rao
  • 595
  • 5
  • 4
1

Here's a concise and highly optimized NSString category:

@implementation NSString (PMUtils)
- (NSString *)sha1Hash
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    NSData *hash = [data sha1Hash];
    return [hash hexString];
} 
@end

@implementation NSData (PMUtils)
- (NSString *) hexString
{
    NSUInteger bytesCount = self.length;
    if (bytesCount) {
        static char const *kHexChars = "0123456789ABCDEF";
        const unsigned char *dataBuffer = self.bytes;
        char *chars = malloc(sizeof(char) * (bytesCount * 2 + 1));
        char *s = chars;
        for (unsigned i = 0; i < bytesCount; ++i) {
            *s++ = kHexChars[((*dataBuffer & 0xF0) >> 4)];
            *s++ = kHexChars[(*dataBuffer & 0x0F)];
            dataBuffer++;
        }
        *s = '\0';
        NSString *hexString = [NSString stringWithUTF8String:chars];
        free(chars);
        return hexString;
    }
    return @"";
}
- (NSData *)sha1Hash
{
    unsigned char digest[CC_SHA1_DIGEST_LENGTH];
    if (CC_SHA1(self.bytes, (CC_LONG)self.length, digest)) {
        return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
    }
    return nil;
}
@end
Peter
  • 2,005
  • 1
  • 20
  • 14
1

I'm seeing a few different possible improvements to the answers in this post.

  1. Never make an unprefixed category. What if you implement -[NSString sha1Hash] in your library, and another library in the same app implements the same method with slightly different semantics? Which one is used will be random, and lead to hard-to-diagnose errors.
  2. If you want a string, there is base64-encoding in the standard library nowadays. Less work than manually building hex stringification.

Here's my solution, adapted from the excellent SocketRocket library's SRHash.m:

// NSString+Sha1Digest.h
#import <Foundation/Foundation.h>

@interface NSString (LBDigest)
- (NSString *)lb_digestString;
@end

@interface NSData (LBDigest)
- (NSString *)lb_digestString;
@end

// NSString+SHA1Digest.m
#import "NSString+Sha1Digest.h"
#import <CommonCrypto/CommonDigest.h>

static NSData *LBSHA1HashFromBytes(const char *bytes, size_t length)
{
    uint8_t outputLength = CC_SHA1_DIGEST_LENGTH;
    unsigned char output[outputLength];
    CC_SHA1(bytes, (CC_LONG)length, output);

    return [NSData dataWithBytes:output length:outputLength];
}

static NSData *LBSHA1HashFromString(NSString *string)
{
    size_t length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    return LBSHA1HashFromBytes(string.UTF8String, length);
}

@implementation NSData (LBDigest)
- (NSString *)lb_digestString;
{
    return [LBSHA1HashFromBytes(self.bytes, self.length) base64EncodedStringWithOptions:0];
}
@end

@implementation NSString (LBDigest)
- (NSString *)lb_digestString;
{
    return [LBSHA1HashFromString(self) base64EncodedStringWithOptions:0];
}
@end
nevyn
  • 7,052
  • 3
  • 32
  • 43