关于C#:iPhone上的NSString的AES加密

AES Encryption for an NSString on the iPhone

有人能给我指出正确的方向来加密一个字符串,用加密的数据返回另一个字符串吗?(我一直在尝试使用aes256加密。)我想写一个方法,它使用两个nsstring实例,一个是要加密的消息,另一个是要用"密码"加密的消息。我怀疑我必须用密码生成加密密钥,如果密码随加密数据一起提供,这种方法可以逆转。然后,该方法应返回从加密数据创建的nsstring。

我已经尝试过这篇文章第一条评论中详细介绍的技术,但到目前为止我还没有运气。苹果的密码练习当然有点道理,但我搞不懂…我见过很多关于cccrypt的引用,但在我使用过的每个案例中都失败了。

我还必须能够解密加密的字符串,但我希望这和kccencrypt/kccdecrypt一样简单。


由于您没有发布任何代码,所以很难确切知道您遇到了哪些问题。然而,你链接到的博客帖子似乎工作得相当不错…除了对CCCrypt()的每个调用中导致编译错误的额外逗号之外。

稍后对这篇文章的评论中包含了这个适用于我的代码,看起来更直接一些。如果将它们的代码包含在nsdata类别中,则可以这样编写:(注意:printf()调用仅用于演示不同点上的数据状态—在实际应用程序中,打印这些值是没有意义的。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString *key = @"my password";
    NSString *secret = @"text to encrypt";

    NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding];
    NSData *cipher = [plain AES256EncryptWithKey:key];
    printf("%s
"
, [[cipher description] UTF8String]);

    plain = [cipher AES256DecryptWithKey:key];
    printf("%s
"
, [[plain description] UTF8String]);
    printf("%s
"
, [[[NSString alloc] initWithData:plain encoding:NSUTF8StringEncoding] UTF8String]);

    [pool drain];
    return 0;
}

考虑到这段代码,加上加密数据并不总是能很好地转换成nsstring,编写两种方法来包装您所需的功能(正向和反向)可能会更方便…

1
2
3
4
5
6
7
8
- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
    return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}

- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
    return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
                                  encoding:NSUTF8StringEncoding] autorelease];
}

这绝对适用于雪豹,@boz报告说CommonCrypto是iPhone核心操作系统的一部分。10.4和10.5都有/usr/include/CommonCrypto,尽管10.5有CCCryptor.3cc的手册页,10.4没有,所以ymmv。

编辑:请参阅下面的问题:使用base64编码将加密数据字节表示为使用安全无损转换的字符串(如果需要)。


我收集了一组NSdata和NSstring的类别,它们使用了Jeff Lamarche的博客中的解决方案,以及Quinn Taylor在Stack Overflow中给出的一些提示。

它使用类别扩展nsdata以提供aes256加密,还提供nsstring到base64的扩展,将加密数据安全编码到字符串。

下面是一个用于加密字符串的示例:

1
2
3
4
5
6
7
8
9
NSString *plainString = @"This string will be encrypted";
NSString *key = @"YourEncryptionKey"; // should be provided by a user

NSLog( @"Original String: %@", plainString );

NSString *encryptedString = [plainString AES256EncryptWithKey:key];
NSLog( @"Encrypted String: %@", encryptedString );

NSLog( @"Decrypted String: %@", [encryptedString AES256DecryptWithKey:key] );

在此处获取完整的源代码:

https://gist.github.com/838614

感谢您提供的所有有用提示!

——米迦勒


@关于您对"给定答案之一的密码安全变体"的请求,请参阅rncryptor。它被设计为完全按照您的请求执行(并且是为响应这里列出的代码的问题而构建的)。

RNCryptor使用含盐的pbkdf2,提供随机的IV,并附加HMAC(也由pbkdf2生成,带有自己的盐)。它支持同步和异步操作。


我在@quinntaylor上等待了一段时间更新他的答案,但是因为他没有更新,所以这里的答案更清晰,并且可以加载到xcode7(也许更大)。我在一个Cocoa应用程序中使用过这个,但是它也可以在iOS应用程序中正常工作。没有弧错误。

粘贴到appdelegate.m或appdelegate.mm文件中的任何@implementation节之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (AES256)

- (NSData *)AES256EncryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES256,
                                     NULL /* initialization vector (optional) */,
                                     [self bytes], dataLength, /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

- (NSData *)AES256DecryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // fetch key data
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES256,
                                     NULL /* initialization vector (optional) */,
                                     [self bytes], dataLength, /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesDecrypted);

    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

@end

将这两个函数粘贴到您想要的@implementation类中。在我的例子中,我在appdelegate.m m或appdelegate.m文件中选择了@implementation appdelegate。

1
2
3
4
5
6
7
8
9
- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
    NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
    return [data base64EncodedStringWithOptions:kNilOptions];
}

- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
    NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions];
    return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding];
}