iOS中的AES256 NSString加密

14

我的应用使用 256位加密算法对一个NSString进行加密和解密(或者说应该这样做)。当我运行我的项目并运行加密方法时,没有任何东西被加密,文本框只是清空了。这是我的代码:

-(void)EncryptText {
    //Declare Keyword and Text
    NSString *plainText = DataBox.text;
    NSString *keyword = Keyword.text;

    //Convert NSString to NSData
    NSData *plainData = [plainText dataUsingEncoding:NSUTF8StringEncoding];

    //Encrypt the Data
    NSData *encryptedData = [plainData AESEncryptWithPassphrase:keyword];

    //Convert the NSData back to NSString
    NSString* cypherText = [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding];

    //Place the encrypted sting inside the Data Box
    NSLog(@"Cipher Text: %@", cypherText);
}

点击这个链接可以下载头文件:包含AES实现的ZIP文件

有人告诉我,我需要对字符串进行Base-64编码才能得到任何结果。如果这是真的,那我该怎么做呢?

还有人告诉我,iOS 5中加密方式发生了改变,而我的应用只支持iOS 5及以上版本。如果这是真的,我该怎么才能在iOS 5上使这个加密工作或者在哪里可以找到另一个能在NSString上使用的AES 256位实现?

为什么这段代码没有产生结果?

2个回答

11

编辑:下面的链接是旧版实现。最新版本称为RNCryptor

你的代码没有使用iOS内置的AES实现,而是有自己的自定义实现。 AESEncryptWithPassphrase:还错误地生成密钥,使大部分口令熵都被丢弃了。

在iOS上,应使用CCCrypt*()函数进行AES加密。还应确保您了解加密和解密例程中正在发生的事情。很容易编写外观正确的加密代码(即您无法通过检查输出来读取它),但它却极其不安全。

请参见使用CommonCrypto正确定义的AES加密,了解上述实现存在的问题以及如何在iOS上正确使用AES。请注意,iOS 5现在可以使用CCKeyDerivationPBKDF

在加密之前,没有要求对字符串进行Base-64编码。 Base-64编码用于将二进制数据转换为可以轻松发送到电子邮件或其他控制字符会成问题的地方的形式。 它将8位二进制数据转换为7位ASCII数据。 在这里不是必要或有用的。


编辑:仔细阅读如何使用此代码的说明非常重要。简单地剪切和粘贴安全代码并希望它能够工作是很危险的。话虽如此,RNCryptManager 的完整源代码可作为iOS 5编程突破极限第11章示例代码的一部分,并且可能会有所帮助。[编辑:这是旧代码;我现在推荐使用 RNCryptor,链接在答案顶部]。该书(尽管网站上说不会在下周发布)包括更长的讨论如何使用此代码,包括如何提高性能和处理非常大的数据集。


在哪里能够放置这段代码:http://robnapier.net/blog/aes-commoncrypto-564?它不能被放进一个独立的类中,因为没有头文件和实现文件与之相配对,那么应该把它放在跟我的方法在同一个.m文件中吗? - Sam Spencer
通常我会把它放在一个名为RNCryptManager的类中,但你可以把它放在任何地方。这段代码也可以轻松地转换成函数而不是类方法。 - Rob Napier
嗯...好吧,当我这样做时,将所有代码放入RNCryptManager.m中,我会得到3个错误,指向“没有已知的选择器类方法:”Random、salt或AES密码。 - Sam Spencer
你是否创建了一个@interface块来定义相关的方法?请参考答案底部上面的编辑。它包含了RNCryptManager类的完整示例。 - Rob Napier

8

使用类别的NSData非常适合进行AES加密,我没有检查过zip文件,但这对您应该是起作用的;

#import <CommonCrypto/CommonCryptor.h>
@implementation NSData (AESAdditions)
- (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

使用它的包装函数,例如;
- (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];
}

1
请注意,此实现非常不安全,因为它丢弃了大部分的密钥空间,没有初始化向量(IV),也没有盐或密钥拉伸。请参阅http://robnapier.net/blog/aes-commoncrypto-564,了解为什么要避免复制此常见代码片段的讨论。 - Rob Napier
@tylerdurden 你应该检查密钥的长度,因为如果 key.length > (kCCKeySizeAES256 + 1),"getCString:maxlength:encoding" 将不起作用,加密器将使用 32 个 '\0' 作为其密钥。这是很危险的。 - MasterBeta

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