使用AES-128和密钥加密NSString [英] Encrypt NSString using AES-128 and a key

查看:214
本文介绍了使用AES-128和密钥加密NSString的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个基本笔记应用,我想允许用户拥有加密或安全的笔记。我有一个UI鞭打了,但现在,我似乎不能得到加密工作。它要么返回我一堆垃圾或什么也没有。这是我用来加密/解密:

   - (BOOL)encryptWithAES128Key:(NSString *)key {
//key对于AES128应该是16字节,否则为null-padded
char * keyPtr [kCCKeySizeAES128 + 1]; //用于终止符的空间(未使用)
bzero(keyPtr,sizeof(keyPtr)); //填充零(用于填充)

//获取关键数据
[key getCString:keyPtr maxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];

//就地加密,因为这是一个可变的数据对象
size_t numBytesEncrypted = 0;
CCCryptorStatus result = CCCrypt(kCCEncrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,
keyPtr,kCCKeySizeAES128,
NULL / *初始化向量(可选)* /,
[self mutableBytes] ],/ * input * /
[self mutableBytes],[self length] + kCCBlockSizeAES128,/ * output * /
& numBytesEncrypted);

return(result == kCCSuccess);
}

- (NSMutableData *)decryptWithAES128Key:(NSString *)key {
//'key'应该是AES128的16个字节,否则为null b $ b char * keyPtr [kCCKeySizeAES128 + 1]; //用于终止符的空间(未使用)
bzero(keyPtr,sizeof(keyPtr)); //填充零(用于填充)

//获取关键数据
[key getCString:keyPtr maxLength:sizeof(keyPtr)encoding:NSUTF8StringEncoding];

//就地加密,因为这是一个可变的数据对象
size_t bufferSize = [self length] + kCCBlockSizeAES128;
void * buffer = malloc(bufferSize);

size_t numBytesEncrypted = 0;
CCCryptorStatus result = CCCrypt(kCCDecrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,
keyPtr,kCCKeySizeAES128,
NULL / *初始化向量(可选)* /,
[自身字节] ],/ * input * /
buffer,bufferSize,/ * output * /
& numBytesEncrypted);


if(result == kCCSuccess || result == kCCParamError){
return [[NSMutableData dataWithBytesNoCopy:buffer length:numBytesEncrypted] retain];
}

return nil;
}

有没有人知道为什么这可能会出错?



编辑1:
我已将我的编码/解密码修改为相同。下面是它现在的样子:

   - (BOOL)encryptWithAES128Key:(NSString *)key {
CCCryptorStatus ccStatus = kCCSuccess;
//对称加密引用。
CCCryptorRef thisEncipher = NULL;
//密文文本容器。
NSData * cipherOrPlainText = nil;
//指向输出缓冲区的指针。
uint8_t * bufferPtr = NULL;
//缓冲区的总大小。
size_t bufferPtrSize = 0;
//要执行的剩余字节。
size_t remainingBytes = 0;
//移动到缓冲区的字节数。
size_t movedBytes = 0;
// plainText缓冲区的长度。
size_t plainTextBufferSize = 0;
//占总写入的占位符。
size_t totalBytesWritten = 0;
//友好的帮助指针。
uint8_t * ptr;

//初始化向量;在这种情况下为0。
uint8_t iv [kCCBlockSizeAES128];
memset((void *)iv,0x0,(size_t)sizeof(iv));
plainTextBufferSize = [self length];

ccStatus = CCCryptorCreate(kCCEncrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,(const void *)[key UTF8String],kCCKeySizeAES128,(const void *)iv,& thisEncipher);

//计算通过并包括final的所有调用的字节块对齐。
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher,plainTextBufferSize,true);

//分配缓冲区。
bufferPtr = [self mutableBytes];

//清零缓冲区。
// memset((void *)bufferPtr,0x0,bufferPtrSize);

//初始化一些必要的书本。

ptr = bufferPtr;

//设置初始尺寸。
remainingBytes = bufferPtrSize;

//实际执行加密或解密。
ccStatus = CCCryptorUpdate(thisEncipher,(const void *)[self bytes],plainTextBufferSize,ptr,remainingBytes,& movedBytes);

ptr + = movedBytes;
remainingBytes - = movedBytes;
totalBytesWritten + = movedBytes;

//将所有内容完成到输出缓冲区。
ccStatus = CCCryptorFinal(thisEncipher,ptr,remainingBytes,& movedBytes);

cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
NSLog(@data:%@,cipherOrPlainText);

NSLog(@buffer:%s,bufferPtr);

CCCryptorRelease(thisEncipher);
thisEncipher = NULL;
if(bufferPtr)free(bufferPtr);
}

- (NSMutableData *)decryptWithAES128Key:(NSString *)key {
CCCryptorStatus ccStatus = kCCSuccess;
//对称加密引用。
CCCryptorRef thisEncipher = NULL;
//密文文本容器。
NSData * cipherOrPlainText = nil;
//指向输出缓冲区的指针。
uint8_t * bufferPtr = NULL;
//缓冲区的总大小。
size_t bufferPtrSize = 0;
//要执行的剩余字节。
size_t remainingBytes = 0;
//移动到缓冲区的字节数。
size_t movedBytes = 0;
// plainText缓冲区的长度。
size_t plainTextBufferSize = 0;
//占总写入的占位符。
size_t totalBytesWritten = 0;
//友好的帮助指针。
uint8_t * ptr;

//初始化向量;在这种情况下为0。
uint8_t iv [kCCBlockSizeAES128];
memset((void *)iv,0x0,(size_t)sizeof(iv));
plainTextBufferSize = [self length];

ccStatus = CCCryptorCreate(kCCDecrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,(const void *)[key UTF8String],kCCKeySizeAES128,(const void *)iv,& thisEncipher

//计算通过并包括final的所有调用的字节块对齐。
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher,plainTextBufferSize,true);

//分配缓冲区。
bufferPtr = malloc(bufferPtrSize * sizeof(uint8_t));

//清零缓冲区。
memset((void *)bufferPtr,0x0,bufferPtrSize);

//初始化一些必要的书本。

ptr = bufferPtr;

//设置初始大小。
remainingBytes = bufferPtrSize;

//实际执行加密或解密。
ccStatus = CCCryptorUpdate(thisEncipher,(const void *)[self bytes],plainTextBufferSize,ptr,remainingBytes,& movedBytes);

ptr + = movedBytes;
remainingBytes - = movedBytes;
totalBytesWritten + = movedBytes;

//将所有内容完成到输出缓冲区。
ccStatus = CCCryptorFinal(thisEncipher,ptr,remainingBytes,& movedBytes);

cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
NSLog(@data:%@,cipherOrPlainText);

NSLog(@buffer:%s,bufferPtr);

CCCryptorRelease(thisEncipher);
thisEncipher = NULL;
if(bufferPtr)free(bufferPtr);

return [NSMutableData dataWithData:cipherOrPlainText];
}

此代码有点如果我使用密码1234567890123456加密此字符串:

 <!DOCTYPE plist PUBLIC -  // Apple // DTD PLIST 1.0 // ENhttp://www.apple.com/DTDs/PropertyList-1.0.dtd\"> 
< plist version =1.0>
< dict>
< key> dict< / key>
< dict>
< key>装置< / key>
< string> Tristan's Magical Macbook of Death< / string>
< key>文字< / key>
<串> e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxMDM4XGNvY29hc3VicnRm
MzYwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTt9Cntc
Y29sb3J0Ymw7XHJlZDI1NVxncmVlbjI1NVxibHVlMjU1O30KXHBhcmRcdHg1NjBc
dHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgw
XHR4NTA0MFx0eDU2MDBcdHg2MTYwXHR4NjcyMFxxbFxxbmF0dXJhbFxwYXJkaXJu
YXR1cmFsCgpcZjBcZnMyNCBcY2YwIFx1bCBcdWxjMCBCTEFILn0 =
< /串>
< key> title< / key>
< string>欢迎使用记事本!< / string>
< key> uuid< / key>
< string> 5yvghz9n4ukgefnbx0qa2xne3nxeebcmcvpci9j5lwpncul1asftdayjv8a< / string>
< / dict>
< key>文字< / key>
<串> e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxMDM4XGNvY29hc3VicnRm
MzYwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTt9Cntc
Y29sb3J0Ymw7XHJlZDI1NVxncmVlbjI1NVxibHVlMjU1O30KXHBhcmRcdHg1NjBc
dHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgw
XHR4NTA0MFx0eDU2MDBcdHg2MTYwXHR4NjcyMFxxbFxxbmF0dXJhbFxwYXJkaXJu
YXR1cmFsCgpcZjBcZnMyNCBcY2YwIFx1bCBcdWxjMCBCTEFILn0 =
< /串>
< key> title< / key>
< string>欢迎使用记事本!< / string>
< key> uuid< / key>
< string> 5yvghz9n4ukgefnbx0qa2xne3nxeebcmcvpci9j5lwpncul1asftdayjv8a< / string>
< / dict>
< / plist>



我得到相同的文本,但整个< / plist& 缺失,并且< / dict> 被切断。解密和打印结果字符串,当用密码0987654321123456或任何其他密码短语加密时,或者当复制到密码字段时,与上面的密码一样,会给我完全的垃圾。

解决方案

两个版本都有相同的问题:你告诉CommonCrypto写入缓冲区的结尾,然后忽略结果。



第一个版本:

  [self mutableBytes],[self length] + kCCBlockSizeAES128,/ * output * / 

第二个版本:

  //计算所有调用的字节块对齐,并包括final。 
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher,plainTextBufferSize,true);

//分配缓冲区。
bufferPtr = [self mutableBytes];

这不对。你没有分配任何东西。你要告诉它把 bufferPtrSize 字节写入大小 [self length]



你想做更多这样的事情(如果你真的想要就地加密):

  //计算所有调用的字节块对齐,并包括final。 
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher,plainTextBufferSize,true);
//如果需要增加我的大小:
if(bufferPtrSize> self.length){
self.length = bufferPtrSize;
}

我也不知道为什么加密是在原地,而解密不是;



您的第二个版本还有其他问题:




  • 你释放了你没有分配的东西: if(bufferPtr)free(bufferPtr);

  • 读取超过字符串的末尾:(const void *)[key UTF8String],kCCKeySizeAES128



其他加密问题:




  • 键应该是固定大小的,并且具有相当大的熵。将字符串转换为字节不是一个好的键(对于一个,长于16个字节的键被有效地截断)。你可以做的最少是哈希。你可能还想迭代哈希,或者只是使用PBKDF2(诚然,我还没有找到PBKDF2的规格/测试矢量...)

  • 你几乎肯定想使用随机IV(参见SecRandomCopyBytes)。



附录



您看到截断结果的原因是因为您返回一个截断的答案(使用PKCS7填充,加密的结果总是大于原始数据)。机会(大约255/256)是最后一个密文块被错误填充(因为你给了CCryptor截断的数据),所以 ccStatus 说发生错误,但你忽略了这个并返回结果无论如何。这是不可思议的坏习惯。 (此外,你真的想使用MAC与CBC,以避免

编辑: p>

似乎工作的代码看起来像这样(包含测试用例):



注意:




  • 在iOS上没有实际测试(虽然非iOS代码应该在iOS上运行;这只是SecRandomCopyBytes是一个稍微更好的界面,但在操作系统上不可用

  • read()循环可能正确,但未经过彻底测试。

  • 密文以IV为前缀。这是教科书的方法,但是使密文更大。

  • 没有身份验证,因此这个代码可以作为填充的oracle。

  • 不支持AES-192或AES-256。这不是很难添加(你只需要打开密钥长度,并适当选择算法)。

  • 键被指定为NSData,所以你需要做 [string dataUsingEncoding:NSUTF8StringEncoding] 。对于奖励积分,通过CC_SHA256运行它,并获取前16个输出字节。

  • 没有就地操作。我不认为这是值得的。



  #include< Foundation / Foundation.h> 
#include< CommonCrypto / CommonCryptor.h>

#if TARGET_OS_IPHONE
#include< Security / SecRandom.h>
#else
#include< fcntl.h>
#include< unistd.h>
#endif

@interface NSData(AES)
- (NSData *)encryptedDataUsingAESKey:(NSData *)key;
- (NSData *)decryptptedDataUsingAESKey:(NSData *)key;
@end
@implementation NSData(AES)

- (NSData *)encryptedDataUsingAESKey:(NSData *)key {
uint8_t iv [kCCBlockSizeAES128];
#if TARGET_OS_IPHONE
if(0!= SecRandomCopyBytes(kSecRandomDefault,sizeof(iv),iv))
{
return nil;
}
#else
{
int fd = open(/ dev / urandom,O_RDONLY);
if(fd< 0){return nil; }
ssize_t bytesRead;
for(uint8_t * p = iv;(bytesRead = read(fd,p,iv + sizeof(iv)-p)); p + =(size_t)bytesRead){
// 0表示EOF 。
if(bytesRead == 0){close(fd); return nil; }
// -1,EINTR意味着我们在读取任何数据之前得到一个系统调用。
//假设我们读取0个字节(因为我们已经处理了EOF)。
if(bytesRead< 0&& errno == EINTR){bytesRead = 0; }
//其他错误是真正的错误。
if(bytesRead< 0){close(fd); return nil; }
}
close(fd);
}
#endif
size_t retSize = 0;
CCCryptorStatus result = CCCrypt(kCCEncrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,
[key bytes],[key length],
iv,
[self bytes],[self length],
NULL,0,
& retSize);
if(result!= kCCBufferTooSmall){return nil; }

//使用IV(教科书方法)前缀数据。
//这需要在几个地方添加sizeof(iv);好吧。
void * retPtr = malloc(retSize + sizeof(iv));
if(!retPtr){return nil; }

//复制IV。
memcpy(retPtr,iv,sizeof(iv));

result = CCCrypt(kCCEncrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,
[key bytes],[key length],
iv,
[self bytes] ],
retPtr + sizeof(iv),retSize,
& retSize);
if(result!= kCCSuccess){free(retPtr); return nil; }

NSData * ret = [NSData dataWithBytesNoCopy:retPtr length:retSize + sizeof(iv)];
//如果NSData的分配失败,是否+ [NSData dataWithBytesNoCopy:length:]?
//假设它。
if(!ret){free(retPtr); return nil; }
return ret;
}

- (NSData *)decryptptedDataUsingAESKey:(NSData *)key {
const uint8_t * p = [self bytes];
size_t length = [self length];
if(length< kCCBlockSizeAES128){return nil; }

size_t retSize = 0;
CCCryptorStatus result = CCCrypt(kCCDecrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,
[key bytes],[key length],
p,
p + kCCBlockSizeAES128,length-kCCBlockSizeAES128,
NULL,0,
& retSize);
if(result!= kCCBufferTooSmall){return nil; }

void * retPtr = malloc(retSize);
if(!retPtr){return nil; }

result = CCCrypt(kCCDecrypt,kCCAlgorithmAES128,kCCOptionPKCS7Padding,
[key bytes],[key length],
p,
p + kCCBlockSizeAES128,length-kCCBlockSizeAES128,
retPtr,retSize,
& retSize);
if(result!= kCCSuccess){free(retPtr); return nil; }

NSData * ret = [NSData dataWithBytesNoCopy:retPtr length:retSize];
//如果NSData的分配失败,是否+ [NSData dataWithBytesNoCopy:length:]?
//假设它。
if(!ret){free(retPtr); return nil; }
return ret;
}

@end

void test(NSData * data,NSData * key)
{
NSLog(@%@, %@,data,key);
NSData * enc = [data encryptedDataUsingAESKey:key];
NSLog(@%@,enc);
NSData * dec = [enc decryptptedDataUsingAESKey:key];
NSLog(@%@,dec);
NSLog((data == dec || [data isEqual:dec])?@pass:@FAIL);
}

int main()
{
#define d(x)[NSData dataWithBytesNoCopy :(x)length:sizeof 1 freeWhenDone:0]
[NSAutoreleasePool new];
NSData * key = d(0123456789abcdef);
test([NSData data],key);
test(d(),key);
test(d(a),key);
test(d(0123456789abcde),key);
test(d(0123456789abcdef),key);
test(d(0123456789abcdef0),key);
test(d(0123456789abcdef0123456789abcde),key);
test(d(0123456789abcdef0123456789abcdef),key);
test(d(0123456789abcdef0123456789abcdef0),key);
}


I have a basic notes app, and I want to allow the user to have encrypted or secure notes. I have an UI whipped up, but right now, I can't seem to get encryption working. Either it returns me a bunch of garbage or nothing at all. This is what I use to en/decrypt:

- (BOOL) encryptWithAES128Key: (NSString *) key {
    // 'key' should be 16 bytes for AES128, will be null-padded otherwise
    char * keyPtr[kCCKeySizeAES128+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];

    // encrypts in-place, since this is a mutable data object
    size_t numBytesEncrypted = 0;
    CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128 , kCCOptionPKCS7Padding, 
                                     keyPtr, kCCKeySizeAES128,
                                     NULL /* initialization vector (optional) */, 
                                     [self mutableBytes], [self length], /* input */
                                     [self mutableBytes], [self length] + kCCBlockSizeAES128, /* output */
                                     &numBytesEncrypted);

    return (result == kCCSuccess);
}

- (NSMutableData *) decryptWithAES128Key: (NSString *) key {
    // 'key' should be 16 bytes for AES128, will be null-padded otherwise
    char * keyPtr[kCCKeySizeAES128+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];

    // encrypts in-place, since this is a mutable data object
    size_t bufferSize           = [self length] + kCCBlockSizeAES128;
    void* buffer                = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus result = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
                                     keyPtr, kCCKeySizeAES128,
                                     NULL /* initialization vector (optional) */, 
                                     [self bytes], [self length], /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesEncrypted);   


    if(result == kCCSuccess || result == kCCParamError) {
        return [[NSMutableData dataWithBytesNoCopy:buffer length:numBytesEncrypted] retain];
    }

    return nil;
}

Does anyone have any idea why this might be going wrong?

Edit 1: I've revised my en/decryption code to be the same. Here is how it looks right now:

- (BOOL) encryptWithAES128Key: (NSString *) key {
    CCCryptorStatus ccStatus = kCCSuccess;
    // Symmetric crypto reference.
    CCCryptorRef thisEncipher = NULL;
    // Cipher Text container.
    NSData * cipherOrPlainText = nil;
    // Pointer to output buffer.
    uint8_t * bufferPtr = NULL;
    // Total size of the buffer.
    size_t bufferPtrSize = 0;
    // Remaining bytes to be performed on.
    size_t remainingBytes = 0;
    // Number of bytes moved to buffer.
    size_t movedBytes = 0;
    // Length of plainText buffer.
    size_t plainTextBufferSize = 0;
    // Placeholder for total written.
    size_t totalBytesWritten = 0;
    // A friendly helper pointer.
    uint8_t * ptr;

    // Initialization vector; dummy in this case 0's.
    uint8_t iv[kCCBlockSizeAES128];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));
    plainTextBufferSize = [self length];

    ccStatus = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, (const void *)[key UTF8String], kCCKeySizeAES128, (const void *)iv, &thisEncipher);

    // Calculate byte block alignment for all calls through to and including final.
    bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);

    // Allocate buffer.
    bufferPtr = [self mutableBytes];

    // Zero out buffer.
    //memset((void *)bufferPtr, 0x0, bufferPtrSize);

    // Initialize some necessary book keeping.

    ptr = bufferPtr;

    // Set up initial size.
    remainingBytes = bufferPtrSize;

    // Actually perform the encryption or decryption.
    ccStatus = CCCryptorUpdate(thisEncipher, (const void *) [self bytes], plainTextBufferSize, ptr, remainingBytes, &movedBytes);

    ptr += movedBytes;
    remainingBytes -= movedBytes;
    totalBytesWritten += movedBytes;

    // Finalize everything to the output buffer.
    ccStatus = CCCryptorFinal(thisEncipher, ptr, remainingBytes, &movedBytes);

    cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
    NSLog(@"data: %@", cipherOrPlainText);

    NSLog(@"buffer: %s", bufferPtr);

    CCCryptorRelease(thisEncipher);
    thisEncipher = NULL;
    if(bufferPtr) free(bufferPtr);
}

- (NSMutableData *) decryptWithAES128Key: (NSString *) key {    
    CCCryptorStatus ccStatus = kCCSuccess;
    // Symmetric crypto reference.
    CCCryptorRef thisEncipher = NULL;
    // Cipher Text container.
    NSData * cipherOrPlainText = nil;
    // Pointer to output buffer.
    uint8_t * bufferPtr = NULL;
    // Total size of the buffer.
    size_t bufferPtrSize = 0;
    // Remaining bytes to be performed on.
    size_t remainingBytes = 0;
    // Number of bytes moved to buffer.
    size_t movedBytes = 0;
    // Length of plainText buffer.
    size_t plainTextBufferSize = 0;
    // Placeholder for total written.
    size_t totalBytesWritten = 0;
    // A friendly helper pointer.
    uint8_t * ptr;

    // Initialization vector; dummy in this case 0's.
    uint8_t iv[kCCBlockSizeAES128];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));
    plainTextBufferSize = [self length];

    ccStatus = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, (const void *)[key UTF8String], kCCKeySizeAES128, (const void *)iv, &thisEncipher);

    // Calculate byte block alignment for all calls through to and including final.
    bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);

    // Allocate buffer.
    bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t) );

    // Zero out buffer.
    memset((void *)bufferPtr, 0x0, bufferPtrSize);

    // Initialize some necessary book keeping.

    ptr = bufferPtr;

    // Set up initial size.
    remainingBytes = bufferPtrSize;

    // Actually perform the encryption or decryption.
    ccStatus = CCCryptorUpdate(thisEncipher, (const void *) [self bytes], plainTextBufferSize, ptr, remainingBytes, &movedBytes);

    ptr += movedBytes;
    remainingBytes -= movedBytes;
    totalBytesWritten += movedBytes;

    // Finalize everything to the output buffer.
    ccStatus = CCCryptorFinal(thisEncipher, ptr, remainingBytes, &movedBytes);

    cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
    NSLog(@"data: %@", cipherOrPlainText);

    NSLog(@"buffer: %s", bufferPtr);

    CCCryptorRelease(thisEncipher);
    thisEncipher = NULL;
    if(bufferPtr) free(bufferPtr);

    return [NSMutableData dataWithData:cipherOrPlainText];
}

This code somewhat works. If I encrypt this string with the passphrase '1234567890123456':

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>dict</key>
    <dict>
        <key>device</key>
        <string>Tristan's Magical Macbook of Death</string>
        <key>text</key>
        <string>e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxMDM4XGNvY29hc3VicnRm
MzYwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTt9Cntc
Y29sb3J0Ymw7XHJlZDI1NVxncmVlbjI1NVxibHVlMjU1O30KXHBhcmRcdHg1NjBc
dHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgw
XHR4NTA0MFx0eDU2MDBcdHg2MTYwXHR4NjcyMFxxbFxxbmF0dXJhbFxwYXJkaXJu
YXR1cmFsCgpcZjBcZnMyNCBcY2YwIFx1bCBcdWxjMCBCTEFILn0=
</string>
        <key>title</key>
        <string>Welcome to Notepaddy!</string>
        <key>uuid</key>
        <string>5yvghz9n4ukgefnbx0qa2xne3nxeebcmcvpci9j5lwpncul1asftdayjv8a</string>
    </dict>
    <key>text</key>
    <string>e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxMDM4XGNvY29hc3VicnRm
MzYwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTt9Cntc
Y29sb3J0Ymw7XHJlZDI1NVxncmVlbjI1NVxibHVlMjU1O30KXHBhcmRcdHg1NjBc
dHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgw
XHR4NTA0MFx0eDU2MDBcdHg2MTYwXHR4NjcyMFxxbFxxbmF0dXJhbFxwYXJkaXJu
YXR1cmFsCgpcZjBcZnMyNCBcY2YwIFx1bCBcdWxjMCBCTEFILn0=
</string>
    <key>title</key>
    <string>Welcome to Notepaddy!</string>
    <key>uuid</key>
    <string>5yvghz9n4ukgefnbx0qa2xne3nxeebcmcvpci9j5lwpncul1asftdayjv8a</string>
</dict>
</plist>

I get the same text back, but the entire </plist> is missing and the </dict> is cut off. Decrypting and printing the result string out gives me complete garbage when encrypted with the passphrase '0987654321123456' or any other passphrase, or the same as above when copied into the password field.

解决方案

Both versions have the same problem: You tell CommonCrypto to write past the end of your buffer, and then you ignore the result.

The first version:

[self mutableBytes], [self length] + kCCBlockSizeAES128, /* output */

The second version:

// Calculate byte block alignment for all calls through to and including final.
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);

// Allocate buffer.
bufferPtr = [self mutableBytes];

That's not right. You're not allocating anything. You're telling it to write bufferPtrSize bytes to a buffer of size [self length]!

You want to do something more like this (if you really want to encrypt in-place):

// Calculate byte block alignment for all calls through to and including final.
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true);
// Increase my size if necessary:
if (bufferPtrSize > self.length) {
  self.length = bufferPtrSize;
}

I'm also not sure why encrypting is in-place while decrypting is not; the latter is, if anything, easier to do.

Your second version has an additional problems:

  • You free something you didn't allocate: if(bufferPtr) free(bufferPtr);
  • You potentially read past the end of the string: (const void *)[key UTF8String], kCCKeySizeAES128

Additional crypto problems:

  • Keys are supposed to be fixed-size and have a decent amount of entropy. Naively converting a string to bytes does not a good key make (for one, keys longer than 16 bytes are effectively truncated). The least you could do is hash it. You might also want to iterate the hash, or just use PBKDF2 (admittedly I haven't found the spec/test vectors for PBKDF2...)
  • You almost certainly want to use a random IV too (see SecRandomCopyBytes).

Addendum:

The reason why you're seeing a truncated result is because you're returning a truncated answer (with PKCS7 padding, the encrypted result is always bigger than the original data). Chances (about 255/256) are that the last ciphertext block was incorrectly padded (because you gave CCryptor truncated data), so ccStatus says an error happened but you ignored this and returned the result anyway. This is incredibly bad practice. (Additionally, you really want to use a MAC with CBC to avoid the padding oracle security hole.)

EDIT:

Some code that seems to work looks something like this (complete with test cases):

Notes:

  • Not actually tested on iOS (though the non-iOS code should work on iOS; it's just that SecRandomCopyBytes is a slightly nicer interface but not available on OS X).
  • The read() loop might be right, but is not thoroughly tested.
  • The ciphertext is prefixed with the IV. This is the "textbook" method, but makes ciphertexts bigger.
  • There is no authentication, so this code can act as a padding oracle.
  • There is no support for AES-192 or AES-256. It would not be difficult to add (you'd just need to switch on the key length and pick the algorithm appropriately).
  • The key is specified as a NSData, so you'll need to do something like [string dataUsingEncoding:NSUTF8StringEncoding]. For bonus points, run it through CC_SHA256 and take the first 16 output bytes.
  • There's no in-place operation. I didn't think it was worth it.

.

#include <Foundation/Foundation.h>
#include <CommonCrypto/CommonCryptor.h>

#if TARGET_OS_IPHONE
#include <Security/SecRandom.h>
#else
#include <fcntl.h>
#include <unistd.h>
#endif

@interface NSData(AES)
- (NSData*) encryptedDataUsingAESKey: (NSData *) key;
- (NSData*) decryptedDataUsingAESKey: (NSData *) key;
@end
@implementation NSData(AES)

- (NSData*) encryptedDataUsingAESKey: (NSData *) key {
        uint8_t iv[kCCBlockSizeAES128];
#if TARGET_OS_IPHONE
        if (0 != SecRandomCopyBytes(kSecRandomDefault, sizeof(iv), iv))
        {
                return nil;
        }
#else
        {
                int fd = open("/dev/urandom", O_RDONLY);
                if (fd < 0) { return nil; }
                ssize_t bytesRead;
                for (uint8_t * p = iv; (bytesRead = read(fd,p,iv+sizeof(iv)-p)); p += (size_t)bytesRead) {
                        // 0 means EOF.
                        if (bytesRead == 0) { close(fd); return nil; }
                        // -1, EINTR means we got a system call before any data could be read.
                        // Pretend we read 0 bytes (since we already handled EOF).
                        if (bytesRead < 0 && errno == EINTR) { bytesRead = 0; }
                        // Other errors are real errors.
                        if (bytesRead < 0) { close(fd); return nil; }
                }
                close(fd);
        }
#endif
        size_t retSize = 0;
        CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
                                     [key bytes], [key length],
                                     iv,
                                     [self bytes], [self length],
                                     NULL, 0,
                                     &retSize);
        if (result != kCCBufferTooSmall) { return nil; }

        // Prefix the data with the IV (the textbook method).
        // This requires adding sizeof(iv) in a few places later; oh well.
        void * retPtr = malloc(retSize+sizeof(iv));
        if (!retPtr) { return nil; }

        // Copy the IV.
        memcpy(retPtr, iv, sizeof(iv));

        result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
                                     [key bytes], [key length],
                                     iv,
                                     [self bytes], [self length],
                                     retPtr+sizeof(iv),retSize,
                                     &retSize);
        if (result != kCCSuccess) { free(retPtr); return nil; }

        NSData * ret = [NSData dataWithBytesNoCopy:retPtr length:retSize+sizeof(iv)];
        // Does +[NSData dataWithBytesNoCopy:length:] free if allocation of the NSData fails?
        // Assume it does.
        if (!ret) { free(retPtr); return nil; }
        return ret;
}

- (NSData*) decryptedDataUsingAESKey: (NSData *) key {
        const uint8_t * p = [self bytes];
        size_t length = [self length];
        if (length < kCCBlockSizeAES128) { return nil; }

        size_t retSize = 0;
        CCCryptorStatus result = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
                                     [key bytes], [key length],
                                     p,
                                     p+kCCBlockSizeAES128, length-kCCBlockSizeAES128,
                                     NULL, 0,
                                     &retSize);
        if (result != kCCBufferTooSmall) { return nil; }

        void * retPtr = malloc(retSize);
        if (!retPtr) { return nil; }

        result = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 
                                     [key bytes], [key length],
                                     p,
                                     p+kCCBlockSizeAES128, length-kCCBlockSizeAES128,
                                     retPtr, retSize,
                                     &retSize);
        if (result != kCCSuccess) { free(retPtr); return nil; }

        NSData * ret = [NSData dataWithBytesNoCopy:retPtr length:retSize];
        // Does +[NSData dataWithBytesNoCopy:length:] free if allocation of the NSData fails?
        // Assume it does.
        if (!ret) { free(retPtr); return nil; }
        return ret;
}

@end

void test(NSData * data, NSData * key)
{
        NSLog(@"%@, %@", data, key);
        NSData * enc = [data encryptedDataUsingAESKey:key];
        NSLog(@"%@", enc);
        NSData * dec = [enc decryptedDataUsingAESKey:key];
        NSLog(@"%@", dec);
        NSLog((data == dec || [data isEqual:dec]) ? @"pass" : @"FAIL");
}

int main()
{
#define d(x) [NSData dataWithBytesNoCopy:("" x) length:sizeof("" x)-1 freeWhenDone:0]
        [NSAutoreleasePool new];
        NSData * key = d("0123456789abcdef");
        test([NSData data], key);
        test(d(""), key);
        test(d("a"), key);
        test(d("0123456789abcde"), key);
        test(d("0123456789abcdef"), key);
        test(d("0123456789abcdef0"), key);
        test(d("0123456789abcdef0123456789abcde"), key);
        test(d("0123456789abcdef0123456789abcdef"), key);
        test(d("0123456789abcdef0123456789abcdef0"), key);
}

这篇关于使用AES-128和密钥加密NSString的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆