web-dev-qa-db-ja.com

Cocoaで文字列の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の別の同様の質問への回答からこのコードを取得しましたが、プログラムが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.

意味がわからない。

40
demonslayer319

これは私が使用するカテゴリです:

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
111
Grant Paul

cdespinosaとirskが実際の問題を既に示しているので、GDBトランスクリプトを見てみましょう。

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

digestをC文字列として出力しました。ここで、この文字列が生のバイトであることがわかります。したがって、すべての8進エスケープ(例:\020\225)およびいくつかの句読文字(/および^)。これは、印刷可能なASCII 16進表記ではありません。0バイトがなかったのは幸運です。そうでなければ、ハッシュ全体を印刷しなかったでしょう。

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

finalnilです。上記の文字列は有効なUTF-8ではないため、これは理にかなっています。繰り返しますが、それは単なる生データバイトです。 stringWithUTF8String:には、UTF-8でエンコードされたテキスト文字列が必要です。あなたはそれを与えなかったので、nilを返しました。

生データを渡すには、NSDataを使用します。この場合、16進表現が必要だと思うので、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オブジェクトではないか、1つであったが解放されたためです。この場合、digestはCocoaオブジェクトではないためです。上記のp digest行で示されているように、Cのバイト配列です。

Objective-CはCのスーパーセットであることに注意してください。すべてのCは変更されずに存在します。つまり、C配列(たとえば、char [])とCocoaのNSArrayが並んでいます。さらに、NSArrayはObjective-C言語ではなくCocoaフレームワークから取得されるため、NSArrayオブジェクトをC配列と交換可能にする方法はありません。Cocoa配列で添え字演算子を使用することはできず、Objective-Cを送信することはできませんC配列へのメッセージ。

10
Peter Hosey

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;
}
9
Peter Lapisu

私はこの方法を使用していました:

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

ダイジェストは生のバイナリハッシュへのポインタだと思います。次の行では、UTF-8ストリングとして解釈しようとしていますが、正当なUTF-8エンコード文字シーケンスが含まれていない可能性が高いです。

あなたが望むのは、あなたが適切だと思うアルゴリズムを使用して、16バイトのunsigned charの静的配列を32 ASCII 16進数文字[0-9a-f]に変換することです。

3
cdespinosa

MD5関数はC文字列を返さず、いくつかのバイトへのポインターを返します。文字列として扱うことはできません。

文字列を作成する場合は、それらのバイトの16進値を使用して文字列を作成する必要があります。 NSDataのカテゴリとしてそれを行う1つの方法を次に示します。

#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
Rob Keniger
@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
nrj