How do I create an MD5 Hash of a string in Cocoa?

I know SHA-1 is preferred, but this project requires I use MD5.

#include <openssl/md5.h>

- (NSString*) MD5Hasher: (NSString*) query {
    NSData* hashed = [query dataUsingEncoding:NSUTF8StringEncoding];
    unsigned char *digest = MD5([hashed bytes], [hashed length], NULL);
    NSString *final = [NSString stringWithUTF8String: (char *)digest];
    return final;
}

I got this code from an answer to another similar question on StackOverflow, but I get the following error from GDB when the program breaks at return final;

  • What describes nil best? What's that really?
  • How to detect keystrokes globally in Swift on macOS?
  • Detect Language of NSString
  • Handling NSError when reading from file?
  • How can my OS X app accept drag-and-drop of picture files from Photos.app?
  • Pick random element of NSArray in Objective-C
  • (gdb) p digest
    $1 = (unsigned char *) 0xa06310e4 "\0206b\260/\336\316^\021\b\a/9\310\225\204"
    (gdb) po final
    Cannot access memory at address 0x0
    (gdb) po digest
    
    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_INVALID_ADDRESS at address: 0xb0623630
    0x98531ed7 in objc_msgSend ()
    The program being debugged was signaled while in a function called from GDB.
    GDB has restored the context to what it was before the call.
    To change this behavior use "set unwindonsignal off"
    Evaluation of the expression containing the function
    (_NSPrintForDebugger) will be abandoned.
    

    I can’t make any sense of it.

    9 Solutions Collect From Internet About “How do I create an MD5 Hash of a string in Cocoa?”

    This is the category I use:

    NSString+MD5.h

    @interface NSString (MD5)
    
    - (NSString *)MD5String;
    
    @end
    

    NSString+MD5.m

    #import <CommonCrypto/CommonDigest.h>
    
    @implementation NSString (MD5)
    
    - (NSString *)MD5String {
        const char *cStr = [self UTF8String];
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
    
        return [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]
        ];  
    }
    
    @end
    

    Usage

    NSString *myString = @"test";
    NSString *md5 = [myString MD5String]; // returns NSString of the MD5 of test
    

    cdespinosa and irsk have already shown you your actual problem, so let me go through your GDB transcript:

    (gdb) p digest
    $1 = (unsigned char *) 0xa06310e4 "\0206b\260/\336\316^\021\b\a/9\310\225\204"
    

    You’ve printed digest as a C string. You can see here that this string is raw bytes; hence all the octal escapes (e.g., \020, \225) and the couple of punctuation characters (/ and ^). It is not the printable ASCII hexadecimal representation you were expecting. You’re lucky that there were no zero bytes in it; otherwise, you would not have printed the entire hash.

    (gdb) po final
    Cannot access memory at address 0x0
    

    final is nil. This makes sense, as your string above isn’t valid UTF-8; again, it’s just raw data bytes. stringWithUTF8String: requires a UTF-8-encoded text string; you didn’t give it one, so it returned nil.

    For passing raw data around, you’d use NSData. In this case, I think you want the hex representation, so you’ll need to create that yourself the way irsk showed you.

    Finally, consider how lucky you are that your input didn’t hash to a valid UTF-8 string. If it had, you wouldn’t have noticed this problem. You may want to construct a unit test for this hash method with this input.

    (gdb) po digest
    
    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_INVALID_ADDRESS at address: 0xb0623630
    0x98531ed7 in objc_msgSend ()
    

    Your program crashed (specific problem: “bad access”, “invalid address”) in objc_msgSend. This is because digest either is not a Cocoa/CF object at all or was one but was freed. In this case, it’s because digest is not a Cocoa object; it is a C array of bytes, as shown by your p digest line above.

    Remember, Objective-C is a superset of C. All of C exists unchanged in it. That means there are C arrays (e.g., char []) and Cocoa’s NSArrays side by side. Moreover, since NSArray comes from the Cocoa framework, not the Objective-C language, there’s no way to make NSArray objects interchangeable with C arrays: You can’t use the subscript operator on Cocoa arrays, and you can’t send Objective-C messages to C arrays.

    Facebook uses this

    + (NSString*)md5HexDigest:(NSString*)input {
        const char* str = [input UTF8String];
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5(str, strlen(str), result);
    
        NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
        for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
            [ret appendFormat:@"%02x",result[i]];
        }
        return ret;
    }
    

    Or instance method

    - (NSString *)md5 {
        const char* str = [self UTF8String];
        unsigned char result[CC_MD5_DIGEST_LENGTH];
        CC_MD5(str, (CC_LONG)strlen(str), result);
    
        NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
        for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
            [ret appendFormat:@"%02x",result[i]];
        }
        return ret;
    }
    

    I believe that digest is a pointer to a raw binary hash. In the next line you’re attempting to interpret it as a UTF-8 string, but it is most likely not to contain legal UTF-8-encoded character sequences.

    I expect what you want is to convert the 16-byte static array of unsigned char into 32 ASCII hexadecimal characters [0-9a-f] using whatever algorithm you see fit.

    The MD5 function does not return a C string, it returns a pointer to some bytes. You can’t treat it as a string.

    If you want to create a string, you need to build a string using the hex values of those bytes. Here is one way to do it as a category on NSData:

    #import <CommonCrypto/CommonDigest.h>
    @implementation NSData (MMAdditions)
    - (NSString*)md5String
    {
        unsigned char md5[CC_MD5_DIGEST_LENGTH];
        CC_MD5([self bytes], [self length], md5);
        return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                md5[0], md5[1],
                md5[2], md5[3],
                md5[4], md5[5],
                md5[6], md5[7],
                md5[8], md5[9],
                md5[10], md5[11],
                md5[12], md5[13],
                md5[14], md5[15]
                ];
    }
    @end
    

    I had used this method:

    NSString+MD5.h

    @interface NSString (MD5)
    
    - (NSString *)MD5;
    
    @end
    

    NSString+MD5.m

    #import "NSString+MD5.h"
    #import <CommonCrypto/CommonDigest.h>
    
    @implementation NSString (MD5)
    
    - (NSString *)MD5 {
    
        const char * pointer = self.UTF8String;
        unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
    
        CC_MD5(pointer, (CC_LONG)strlen(pointer), md5Buffer);
    
        NSMutableString * string = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
        for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
            [string appendFormat:@"%02x", md5Buffer[i]];
        }
    
        return string;
    }
    
    @end
    

    Usage:

    NSString * myString = @"test";
    NSString * md5 = [myString MD5];
    
    @implementation NSString (MD5)
    
    + (NSString *)formattedMD5:(const char *)data length:(unsigned long)len
    {
        unsigned char *digest = MD5((unsigned const char *)data, len, NULL);
        NSMutableArray *values = [[NSMutableArray alloc] init];
    
        for (int i = 0; i < strlen((char *)digest); i++)
        {
            char hexValue[4];
            sprintf(hexValue, "%02X", digest[i]);
            [values addObject:[NSString stringWithCString:hexValue length:strlen(hexValue)]];
        }
    
        // returns a formatted MD5 fingerprint like
        //      00:00:00:00:00:00:00:00:00
        return [values componentsJoinedByString:@":"];
    }
    
    @end
    

    These answers are correct but confusing. I post a working sample, since I had issues with most other answers. The code is tested and works well for Mac OS X 10.12.x and iOS 10.1.x.

    YourClass.h

    #import <CommonCrypto/CommonDigest.h>
    @interface YourClass : NSObject
    + (NSString *) md5:(NSString *) input;
    @end
    

    YourClass.m

    #import YourClass.h
    
    + (NSString *) md5:(NSString *) input
    {
        const char *cStr = [input UTF8String];
        unsigned char digest[CC_MD5_DIGEST_LENGTH];
        CC_MD5(cStr, (uint32_t)strlen(cStr), digest);
        NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
        for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
            [output appendFormat:@"%02x", digest[i]]; //%02X for capital letters
        return output;
    }
    

    Usage (e.g. in some other class):

    SomeOtherClass.h

    #import "YourClass.h"
    

    SomeOtherClass.m

    -(void) Test
    {
        //call by [self Test] or use somewhere in your code.
        NSString *password = @"mypassword123";
        NSString *md5 = [YourClass md5:password];
        NSLog(@"%@", password);
        NSLog(@"%@", md5);
    }
    
    - (NSString*)MD5:(NSData *) data
        {
            // Create byte array of unsigned chars
            unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
    
            // Create 16 byte MD5 hash value, store in buffer
            CC_MD5([data bytes], (CC_LONG)data.length, md5Buffer);
    
            // Convert unsigned char buffer to NSString of hex values
            NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
            for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
            [output appendFormat:@"%02x",md5Buffer[i]];
    
            return output;
        }
    

    How to use

    NSString *yourStrToBeConverted;
    NSData* data = [yourStrToBeConverted dataUsingEncoding:NSUTF8StringEncoding];
    NSString *md5res=[self MD5:data];