gpt4 book ai didi

cocoa - 使用 AES-128 和 key 加密 NSString

转载 作者:行者123 更新时间:2023-12-03 16:26:13 25 4
gpt4 key购买 nike

我有一个基本的笔记应用程序,我想允许用户拥有加密或安全的笔记。我已经创建了一个用户界面,但现在我似乎无法使加密工作。要么它给我返回一堆垃圾,要么什么也没有。这是我用来加密/解密的:

- (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:我已将我的加密/解密代码修改为相同。这是现在的样子:

- (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];
}

这段代码在某种程度上有效。如果我使用密码“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>

我得到了相同的文本,但整个 </plist> 丢失并且 </dict> 被切断。当使用密码“0987654321123456”或任何其他密码加密时,或者在复制到密码字段时与上面相同,解密并打印结果字符串会给我带来完全的垃圾。

最佳答案

两个版本都有相同的问题:您告诉 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 字节写入大小为 [self length] 的缓冲区!

您想做类似这样的事情(如果您确实想就地加密):

// 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;
}

我也不确定为什么加密是就地而解密不是;如果有的话,后者更容易做到。

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

  • 释放未分配的内容:if(bufferPtr) free(bufferPtr);
  • 您可能会读到字符串末尾:(const void *)[key UTF8String], kCCKeySizeAES128

其他加密问题:

  • key 应该是固定大小的并且具有相当数量的熵。单纯地将字符串转换为字节并不是一个好的 key 制作(例如,长度超过 16 字节的 key 会被有效地截断)。你至少可以做的就是散列它。您可能还想迭代哈希,或者只使用 PBKDF2(诚然,我还没有找到 PBKDF2 的规范/测试向量...)
  • 您几乎肯定也想使用随机 IV(请参阅 SecRandomCopyBytes)。

附录:

您看到截断结果的原因是因为您返回了截断答案(使用 PKCS7 填充,加密结果始终大于原始数据)。可能性(大约 255/256)是最后一个密文 block 被错误地填充(因为您向 CCryptor 提供了截断的数据),因此 ccStatus 表示发生了错误,但您忽略了这一点并返回了结果。这是非常糟糕的做法。 (此外,您确实希望使用带有 CBC 的 MAC 来避免 padding oracle security hole 。)

编辑:

一些似乎有效的代码看起来像这样(带有测试用例):

注释:

  • 未在 iOS 上进行实际测试(尽管非 iOS 代码应该可以在 iOS 上运行;只是 SecRandomCopyBytes 的界面稍好一些,但在 OS X 上不可用)。
  • read() 循环可能是正确的,但尚未经过彻底测试。
  • 密文以 IV 为前缀。这是“教科书”方法,但会使密文变得更大。
  • 没有身份验证,因此此代码可以充当填充预言机。
  • 不支持 AES-192 或 AES-256。添加并不困难(您只需要打开 key 长度并适当选择算法)。
  • key 被指定为 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*) 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);
}

关于cocoa - 使用 AES-128 和 key 加密 NSString,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6510638/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com