从NSString创建SHA1哈希值

43

我如何从一个NSString创建 SHA1。

假设 NSString 已设置为:

NSString *message = @"Message";

我可以使用PHP创建一个SHA1散列值,代码为sha($message)。但是不幸的是,在Objective-C中它不能这样工作。


1
你可以查看CC_SHA1。此外,也可以在这里查看。 - Alex
一定要将其发布为答案! - Wevah
我非常喜欢hypercrypt的答案,所以我将其打包成一个小的git仓库。请查看Github上的NSString类别(https://github.com/atreat/NSString-Sha1)。同时,如果您有任何其他好的NSString加密,请随意添加。 - atreat
7个回答

93

我将以下内容归类到了NSString类中(可在https://github.com/hypercrypt/NSString-Hashes获取):

#import <CommonCrypto/CommonDigest.h>

...

- (NSString *)sha1
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];

    CC_SHA1(data.bytes, (CC_LONG)data.length, digest);

    NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];

    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
    {
        [output appendFormat:@"%02x", digest[i]];
    }

    return output;
}

从 Xcode 10.0 开始,您应该使用 import CommonCrypto,因为它现在在 Swift 中原生可用!如果您最近迁移到 Xcode 10.0 并且使用旧方法,则可以将此作为更改提示:

Command CompileSwift 失败,退出码为非零


3
这不需要您导入 <CommonCrypto/CommonDigest.h> 吗? - Bill Burgess
12
Xcode 4 抱怨以下代码行: CC_SHA1(data.bytes, data.length, digest);请改为: CC_SHA1(data.bytes, (CC_LONG) data.length, digest); 这样修改后,代码能够正常编译。 - A. R. Younce
为什么你要用CC_SHA1_DIGEST_LENGTH*2来初始化mutableString,而不是直接使用[NSMutableString string]呢? - Oliver
2
它创建一个可变字符串,具有足够的容量来容纳整个输出字符串,这样在循环期间就不需要扩展字符串的存储空间。如果您在循环之前(或者上限)知道所需的大小,通常最好使用NSMutable*类的*WithCapacity:方法,因为可以在开始时分配正确的内存大小。 - hypercrypt

15

我非常喜欢hypercrypt的答案,但是我被鼓励发表我的评论。

你可以查看CC_SHA1或者这个相关的SO问题。


13
- (NSString *)sha1:(NSString *)str {
const char *cStr = [str UTF8String];
unsigned char result[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(cStr, strlen(cStr), result);
NSString *s = [NSString  stringWithFormat:
           @"%02x%02x%02x%02x%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],
           result[16], result[17], result[18], result[19]
           ];

return s;
}

4

我花了一些时间将@hypercrypt的解决方案移植到了Swift,所以我决定与其他可能遇到同样问题的人分享。

需要注意的一件重要事情是,你需要CommonCrypto库,但该库没有Swift模块。最简单的解决方法是在桥接头中导入它:

#import <CommonCrypto/CommonCrypto.h>

一旦导入,你就不需要其他东西了。只需使用提供的String扩展即可:

extension String
{
    func sha1() -> String
    {
        var selfAsSha1 = ""

        if let data = self.dataUsingEncoding(NSUTF8StringEncoding)
        {
            var digest = [UInt8](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
            CC_SHA1(data.bytes, CC_LONG(data.length), &digest)

            for index in 0..<CC_SHA1_DIGEST_LENGTH
            {
                selfAsSha1 += String(format: "%02x", digest[Int(index)])
            }
        }

        return selfAsSha1
    }
}

请注意,我的解决方案没有考虑 NSMutableString 在原帖中保留容量的影响。然而,我怀疑任何人都看不出区别 :)

很好,但为什么需要扩展? - zaph
主要是因为原始答案使用了Category,所以我脑海中的常见关联是Extension :) 我认为这很容易实现,并且只是为String添加所需的功能。如果有更好的解决方案,我也很乐意接受 :) - hris.to

3

试试这个:

#import <CommonCrypto/CommonDigest.h>

-(NSData *) selector
{
    unsigned char hashBytes[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1([dataToHash bytes], [dataToHash length], hashBytes);
    NSData *data = [[NSData alloc] initWithBytes:hashBytes length:CC_SHA1_DIGEST_LENGTH];
}

2
我在这篇文章中看到了几个可能的改进点。
  1. 不要创建未加前缀的类别。如果你在库中实现了-[NSString sha1Hash]方法,而同一应用程序中的另一个库实现了具有略微不同语义的相同方法,那么使用哪个方法将是随机的,并导致难以诊断的错误。
  2. 如果您需要一个字符串,现在标准库中有base64编码。比手动构建十六进制字符串更简单。

以下是我的解决方案,改编自优秀的SocketRocket库的SRHash.m:

// NSString+Sha1Digest.h
#import <Foundation/Foundation.h>

@interface NSString (LBDigest)
- (NSString *)lb_digestString;
@end

@interface NSData (LBDigest)
- (NSString *)lb_digestString;
@end

// NSString+SHA1Digest.m
#import "NSString+Sha1Digest.h"
#import <CommonCrypto/CommonDigest.h>

static NSData *LBSHA1HashFromBytes(const char *bytes, size_t length)
{
    uint8_t outputLength = CC_SHA1_DIGEST_LENGTH;
    unsigned char output[outputLength];
    CC_SHA1(bytes, (CC_LONG)length, output);

    return [NSData dataWithBytes:output length:outputLength];
}

static NSData *LBSHA1HashFromString(NSString *string)
{
    size_t length = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    return LBSHA1HashFromBytes(string.UTF8String, length);
}

@implementation NSData (LBDigest)
- (NSString *)lb_digestString;
{
    return [LBSHA1HashFromBytes(self.bytes, self.length) base64EncodedStringWithOptions:0];
}
@end

@implementation NSString (LBDigest)
- (NSString *)lb_digestString;
{
    return [LBSHA1HashFromString(self) base64EncodedStringWithOptions:0];
}
@end

1

这是一个简洁且高度优化的NSString类别

@implementation NSString (PMUtils)
- (NSString *)sha1Hash
{
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    NSData *hash = [data sha1Hash];
    return [hash hexString];
} 
@end

@implementation NSData (PMUtils)
- (NSString *) hexString
{
    NSUInteger bytesCount = self.length;
    if (bytesCount) {
        static char const *kHexChars = "0123456789ABCDEF";
        const unsigned char *dataBuffer = self.bytes;
        char *chars = malloc(sizeof(char) * (bytesCount * 2 + 1));
        char *s = chars;
        for (unsigned i = 0; i < bytesCount; ++i) {
            *s++ = kHexChars[((*dataBuffer & 0xF0) >> 4)];
            *s++ = kHexChars[(*dataBuffer & 0x0F)];
            dataBuffer++;
        }
        *s = '\0';
        NSString *hexString = [NSString stringWithUTF8String:chars];
        free(chars);
        return hexString;
    }
    return @"";
}
- (NSData *)sha1Hash
{
    unsigned char digest[CC_SHA1_DIGEST_LENGTH];
    if (CC_SHA1(self.bytes, (CC_LONG)self.length, digest)) {
        return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
    }
    return nil;
}
@end

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