Cocoa 使用AES-128和密钥加密NSString
我有一个基本的notes应用程序,我想允许用户拥有加密或安全的notes。我有一个用户界面,但现在,我似乎无法让加密工作。它要么给我一堆垃圾,要么什么都没有。这是我用来加密/解密的: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
- (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;
}
}
}