如何在Cocoa中创建字符串的MD5哈希?

时间:2010-01-07 06:09:15

标签: objective-c cocoa hash md5

我知道SHA-1是首选,但这个项目要求我使用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;
}

我从StackOverflow上另一个类似问题的答案中得到了这段代码,但是当程序在返回final时断开时,我从GDB得到以下错误;

(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.

我无法理解它。

9 个答案:

答案 0 :(得分:111)

这是我使用的类别:

<强>的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

<强>用法

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

答案 1 :(得分:10)

cdespinosa和irsk已经向您展示了您的实际问题,所以让我查看您的GDB成绩单:

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

您已将digest打印为C字符串。你可以在这里看到这个字符串是原始字节;因此所有八进制转义(例如\020\225)和标点符号(/^)。它不是您期望的可打印ASCII十六进制表示。你很幸运,它没有零字节;否则,你就不会打印整个哈希值。

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

finalnil。这是有道理的,因为上面的字符串无效UTF-8;再次,它只是原始数据字节。 stringWithUTF8String:需要UTF-8编码的文本字符串;你没有给它一个,所以它返回了nil

要传递原始数据,您需要使用NSData。在这种情况下,我认为你想要十六进制表示,所以你需要按照irsk向你展示的方式自己创建。

最后,考虑一下你输入没有哈希到有效的UTF-8字符串是多么幸运。如果有,你就不会注意到这个问题。您可能希望使用此输入为此哈希方法构建单元测试。

(gdb) po digest

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

您的程序在objc_msgSend中崩溃(具体问题:“访问权限错误”,“无效地址”)。这是因为digest或者根本不是Cocoa / CF对象,或者是一个但是被释放了。在这种情况下,因为digest不是Cocoa对象;它是一个C字节数组,如上面的p digest行所示。

请记住,Objective-C是C的超集。所有C都保持不变。这意味着有C阵列(例如,char [])和Cocoa的NSArrays并排。此外,由于NSArray来自Cocoa框架,而不是Objective-C语言,因此无法使NSArray对象与C数组互换:您不能在Cocoa数组上使用下标运算符,也不能发送Objective-C消息到C数组。

答案 2 :(得分:9)

Facebook使用此

#import <CommonCrypto/CommonDigest.h>

+ (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;
}

或实例方法

- (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;
}

答案 3 :(得分:3)

我相信摘要是指向原始二进制哈希的指针。在下一行中,您尝试将其解释为UTF-8字符串,但很可能不包含合法的UTF-8编码字符序列。

我希望你想要的是使用你认为合适的任何算法将无符号字符的16字节静态数组转换为32个ASCII十六进制字符[0-9a-f]。

答案 4 :(得分:3)

MD5函数不返回C字符串,它返回指向某些字节的指针。你不能把它当作一个字符串。

如果要创建字符串,则需要使用这些字节的十六进制值构建字符串。以下是将其作为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

答案 5 :(得分:3)

我使用过这种方法:

<强>的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

<强>用法:

NSString * myString = @"test";
NSString * md5 = [myString MD5];

答案 6 :(得分:1)

@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

答案 7 :(得分:-1)

- (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;
    }

如何使用

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

答案 8 :(得分:-1)

这些答案是正确但令人困惑的。我发布了一个工作样本,因为我遇到了大多数其他答案的问题。代码经过测试,适用于Mac OS X 10.12.x和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;
}

用法(例如在其他课程中):

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);
}