【问题标题】:How do I create an MD5 Hash of a string in Cocoa?如何在 Cocoa 中创建字符串的 MD5 哈希?
【发布时间】:2010-01-07 06:09:15
【问题描述】:

我知道首选 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.

我无法理解它。

【问题讨论】:

    标签: objective-c cocoa hash md5


    【解决方案1】:

    这是我使用的类别:

    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
    

    【讨论】:

    • 我导入了 NSString+MD5。但是 Xcode 和编译器说:“没有为 NSString 找到可见接口”
    • 可能使用常量 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 );
    • - (NSString *)MD5String { const char *cStr = [self UTF8String];无符号字符结果[CC_MD5_DIGEST_LENGTH]; CC_MD5( cStr, (int)strlen(cStr), 结果); // 这是 md5 调用 return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[ 0],结果[1],结果[2],结果[3],结果[4],结果[5],结果[6],结果[7],结果[8],结果[9],结果[ 10],结果[11],结果[12],结果[13],结果[14],结果[15]];
    【解决方案2】:

    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 数组。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 如果你不知道常量 CC_MD5_DIGEST_LENGTH 来自哪里,可以通过在你的文件中导入 CommonCrypto/CommonDigest.h 来找到它
      【解决方案4】:

      我用过这个方法:

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

      【讨论】:

      • +1 for (CC_LONG) 反对 strlen(pointer) 以解决 Xcode 中的隐式转换和静音警告。
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        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
        

        【讨论】:

          【解决方案7】:
          @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
          

          【讨论】:

            【解决方案8】:

            这些答案是正确的,但令人困惑。我发布了一个工作示例,因为我对大多数其他答案都有疑问。该代码经过测试,适用于 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);
            }
            

            【讨论】:

              【解决方案9】:
              - (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];
              

              【讨论】:

              • 这个方法怎么调用?
              猜你喜欢
              • 2011-11-29
              • 1970-01-01
              • 2015-01-05
              • 2012-10-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-07-12
              • 1970-01-01
              相关资源
              最近更新 更多