Cocoa 使用AES-128和密钥加密NSString

Cocoa 使用AES-128和密钥加密NSString,cocoa,security,encryption,nsstring,Cocoa,Security,Encryption,Nsstring,我有一个基本的notes应用程序,我想允许用户拥有加密或安全的notes。我有一个用户界面,但现在,我似乎无法让加密工作。它要么给我一堆垃圾,要么什么都没有。这是我用来加密/解密的: - (BOOL) encryptWithAES128Key: (NSString *) key { // 'key' should be 16 bytes for AES128, will be null-padded otherwise char * keyPtr[kCCKeySizeAES128

我有一个基本的notes应用程序,我想允许用户拥有加密或安全的notes。我有一个用户界面,但现在,我似乎无法让加密工作。它要么给我一堆垃圾,要么什么都没有。这是我用来加密/解密的:

- (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;
}
有人知道为什么会出问题吗

编辑1: 我已经修改了我的en/解密代码,使之保持不变。下面是它现在的样子:

- (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];
}
这段代码有些有效。如果我使用密码短语“12345678901213456”加密此字符串:

<!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>

字典
装置
特里斯坦的魔法死亡笔记本
文本
E1XYDGYXXGFUC2LCYW5ZAWWWZZEYNtJCY29JB2FYDYXMD4XGNVY29HC3VICNRM
MZYWCNTCZM9uDhribfxMfxMxMc3dpc3nczMnOyxJzzxQwiehlbhzldgljytt9cntc
Y29SB3J0YMW7XHJLZDI1NVXNCMVLBJI1NVXIBHVLMJU1O30KXHBHCMRCCDHG1NJCB
dHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgw
XHR4NTA0MF0EDU2MDBCDHG2MTYWXHR4NJCYMFXBFXBFXBMF0DXJHBFXWYXJKAXJU
YXR1CMFSCGPCZJBCZNMYNCBCCY2YWIFX1BCBCDWXJMCBCTEFILN0=
标题
欢迎来到Notepaddy!
uuid
5YVGHZ9N4UKGEFNBX0QA2XNE3XEEBCCVPCI9J5LWPNCUL1ASFTDAYJV8A
文本
E1XYDGYXXGFUC2LCYW5ZAWWWZZEYNtJCY29JB2FYDYXMD4XGNVY29HC3VICNRM
MZYWCNTCZM9uDhribfxMfxMxMc3dpc3nczMnOyxJzzxQwiehlbhzldgljytt9cntc
Y29SB3J0YMW7XHJLZDI1NVXNCMVLBJI1NVXIBHVLMJU1O30KXHBHCMRCCDHG1NJCB
dHgxMTIwXHR4MTY4MFx0eDIyNDBcdHgyODAwXHR4MzM2MFx0eDM5MjBcdHg0NDgw
XHR4NTA0MF0EDU2MDBCDHG2MTYWXHR4NJCYMFXBFXBFXBMF0DXJHBFXWYXJKAXJU
YXR1CMFSCGPCZJBCZNMYNCBCCY2YWIFX1BCBCDWXJMCBCTEFILN0=
标题
欢迎来到Notepaddy!
uuid
5YVGHZ9N4UKGEFNBX0QA2XNE3XEEBCCVPCI9J5LWPNCUL1ASFTDAYJV8A
我得到了相同的文本,但是整个
都丢失了,
被切断了。当使用密码短语“0987654321123456”或任何其他密码短语进行加密时,解密并打印结果字符串会给我带来完全的垃圾,或者当复制到密码字段时,会产生与上述相同的垃圾。

关于您的*解*加密代码
kCCParamError
顾名思义是一个错误。你为什么把它当作成功?如果你犯了错误,那就意味着你做错了什么;查看您传递的参数并找出是什么

这可能就是你得到“垃圾”的原因:
CCCrypt
(解密)实际上从未给过你任何东西,因为它无法处理你给它的任何值。您得到的是分配时输出缓冲区中的内容

如果在调用
CCCrypt
并使用其
mutableBytes
作为缓冲区之前切换到
calloc
或创建NSMutableData对象,我想您会发现缓冲区总是包含全零。同样的原因:
CCCrypt
没有填写它,因为它失败了,因为您传递了一个或多个错误的值(参数错误)

您需要先修复参数错误,然后才能期望它正常工作

您可以尝试将
CCCrypt
调用分解为对
CCCryptorCreate
CCCryptorUpdate
CCCryptorFinal
CCCryptorRelease
的调用,至少是暂时性的,以查看出错的地方

加密:相同的问题,或者根本没有问题 您的加密方法是否返回
?我猜它会返回
NO
,因为加密和解密方法之间的代码似乎基本相同,所以无论解密代码中有什么错误,加密代码中也可能有错误。查看
CCCrypt
返回的内容,如果失败,请让它工作

如果它返回
YES
CCCrypt
成功),那么我想知道你所说的“返回给我一堆垃圾”是什么意思。您指的是您将
encryptWithAES128Key:
消息发送到的数据对象的内容吗

如果是这样,那么这就是预期的结果。您的代码对数据对象的内容进行加密,用密文覆盖明文。你看到的不是纯粹的“垃圾”——而是密文!解密它(成功)将再次显示明文


顺便说一句,您在创建输出缓冲区时有“encrypts in place,因为这是一个可变数据对象”的注释,以便在解密代码中不起作用。它应该在加密方法中,您正在使用的地方。我建议要么两个都到位,要么两个都不到位;一致性是一个优点。

两个版本都有相同的问题:您告诉CommonCrypto写超过缓冲区的末尾,然后忽略结果

第一版:

[self mutableBytes], [self length] + kCCBlockSizeAES128, /* output */
第二个版本:

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

// Allocate buffer.
bufferPtr = [self mutableBytes];
那是不对的。你没有分配任何东西。您告诉它将
bufferPtrSize
字节写入大小为
[自身长度]
的缓冲区

您希望执行类似的操作(如果您真的希望就地加密):

我也不知道为什么加密是到位的,而解密不是;后者更容易做到

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

  • 释放未分配的内容:
    if(bufferPtr)free(bufferPtr)
  • 您可能会读取超过字符串结尾的内容:
    (const void*)[key UTF8String],kCCKeySizeAES128
其他加密问题:

  • 密钥应该是固定大小的,并且具有相当大的熵。简单地将字符串转换为字节并不是一个好的键(例如,长度超过16字节的键会被有效地截断)。至少你能做的就是把它弄碎。您可能还想迭代散列,或者只使用PBKDF2(无可否认,我还没有找到PBKDF2的规范/测试向量…)
  • 您几乎肯定也希望使用随机IV(请参阅SecRandomCopyBytes)
附录:

// 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;
}
#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);
}
if (encryptOrDecrypt == kCCEncrypt) {
    if (*pkcs7 != kCCOptionECBMode) {
        if ((plainTextBufferSize % kChosenCipherBlockSize) == 0) {
            *pkcs7 = 0x0000;
        } else {
            *pkcs7 = kCCOptionPKCS7Padding;
        }
    }
}