Как создать MD5-хэш строки в какао? - PullRequest
40 голосов
/ 07 января 2010

Я знаю, что 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, но я получаю следующую ошибку от GDB, когда программа прерывается на return final;

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

111 голосов
/ 07 января 2010

Это категория, которую я использую:

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
10 голосов
/ 07 января 2010

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

final - это nil. Это имеет смысл, так как ваша строка выше не является допустимой 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 либо вообще не является объектом Какао / CF, либо был одним, но был освобожден. В данном случае это потому, что digest не является объектом Какао; это массив байтов Си, как показано вашей строкой p digest выше.

Помните, Objective-C является надмножеством C. Все C существует в нем без изменений. Это означает, что рядом есть C-массивы (например, char []) и NSArrays Cocoa. Более того, поскольку NSArray происходит из среды Cocoa, а не языка Objective-C, невозможно сделать объекты NSArray взаимозаменяемыми с массивами C: вы не можете использовать оператор индексов для массивов Cocoa и не можете отправить Objective-C сообщения в массивы C.

9 голосов
/ 23 августа 2013

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 голосов
/ 19 декабря 2014

Я использовал этот метод:

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];
3 голосов
/ 07 января 2010

Функция 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 голосов
/ 07 января 2010

Я считаю, что дайджест - это указатель на необработанный двоичный хеш. В следующей строке вы пытаетесь интерпретировать ее как строку UTF-8, но, скорее всего, она не содержит допустимых последовательностей символов в кодировке UTF-8.

Я ожидаю, что вам нужно преобразовать 16-байтовый статический массив беззнаковых символов в 32 шестнадцатеричных символа ASCII [0-9a-f], используя любой алгоритм, который вы считаете подходящим.

1 голос
/ 07 января 2010
@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
0 голосов
/ 25 ноября 2016

Эти ответы верны, но сбивают с толку. Я публикую рабочий образец, так как у меня были проблемы с большинством других ответов. Код протестирован и хорошо работает для 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);
}
0 голосов
/ 07 февраля 2015
- (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];
...