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

41

我知道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上类似问题的答案中获取了这段代码,但是当程序在return 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个回答

113

我使用的类别是:

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
我导入了NSString + MD5。但Xcode和编译器说:“未找到NSString的可见接口”。 - gdm
2
也许可以使用常量CC_MD5_DIGEST_LENGTH,并将其转换为CC_LONG以实现64位兼容性。`const char *cStr = [self UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH];CC_MD5(cStr, (CC_LONG)strlen(cStr), result);` - naudecruywagen
  • (NSString *)MD5String { const char *cStr = [self UTF8String]; unsigned char result[CC_MD5_DIGEST_LENGTH]; CC_MD5(cStr, (int)strlen(cStr), result); // 这是 md5 调用 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] ];
}
- zzheads

10

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

2
如果您不知道常量CC_MD5_DIGEST_LENGTH的来源,可以通过在文件中导入CommonCrypto/CommonDigest.h来找到它。 - Christian

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编码的文本字符串;由于你没有提供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 ()
你的程序崩溃了(具体问题:“bad access”,“invalid address”),崩溃在objc_msgSend。这是因为digest要么根本不是Cocoa / CF对象,要么是一个已释放的对象。在这种情况下,是因为digest不是Cocoa对象;正如你上面的p digest行所示,它是一个字节数组的C数组。

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


4

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

使用方法:

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

在Xcode中,为了解决隐式转换并消除警告,请使用(CC_LONG)来代替strlen(pointer) - andrewbuilder

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

3
我认为摘要是一个指向原始二进制哈希值的指针。在下一行中,您试图将其解释为UTF-8字符串,但它很可能不包含合法的UTF-8编码字符序列。
我猜你想把16字节的静态无符号字符数组转换成32个ASCII十六进制字符[0-9a-f],使用任何您认为合适的算法。

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

-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];

如何调用这个方法? - Bijender Singh Shekhawat

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接