Encrypt NSString using AES-128 and a key - cocoa

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

Regarding your *de*cryption code
kCCParamError is, as its name says, an error. Why are you treating it as success? If you get that error, it means you did something wrong; look at the parameters you passed and figure out what.
This is probably why you're getting “garbage”: CCCrypt (decrypting) never actually gave you anything, because it could not work with whatever values you gave it. What you're getting is whatever was lying around in the output buffer when you allocated it.
If you switch to calloc or to creating the NSMutableData object before calling CCCrypt and using its mutableBytes as the buffer, I think you'll find that the buffer then always contains all zeroes. Same reason: CCCrypt is not filling it out, because it's failing, because you passed one or more wrong values (parameter error).
You need to fix the parameter error before you can expect this to work.
You might try breaking the CCCrypt call into calls to CCCryptorCreate, CCCryptorUpdate, CCCryptorFinal, and CCCryptorRelease, at least temporarily, to see where it's going wrong.
Encryption: The same problem, or no problem at all
Is your encryption method returning YES or NO? I'm guessing it returns NO, because the code appears to be mostly the same between the encryption and decryption methods, so whatever you have wrong in your decryption code is probably wrong in your encryption code as well. See what CCCrypt is returning and, if it's failing, get it working.
If it is returning YES (CCCrypt is succeeding), then I wonder what you mean by “returns me a bunch of garbage”. Are you referring to the contents of the data object you sent the encryptWithAES128Key: message to?
If that's the case, then that is the expected result. Your code encrypts the contents of the data object in place, overwriting the cleartext with the ciphertext. What you're seeing isn't pure “garbage”—it's the ciphertext! Decrypting it (successfully) will reveal the cleartext again.
By the way, you have the “encrypts in-place, since this is a mutable data object” comment on the creation of an output buffer in order to not work in-place in the decryption code. It should be in the encryption method, where you are working in-place. I suggest making either both work in-place or neither work in-place; consistency is a virtue.

If you have following padding changes in your code remove it and always keep kCCOptionPKCS7Padding on, this should solve your issue.
if (encryptOrDecrypt == kCCEncrypt) {
if (*pkcs7 != kCCOptionECBMode) {
if ((plainTextBufferSize % kChosenCipherBlockSize) == 0) {
*pkcs7 = 0x0000;
} else {
*pkcs7 = kCCOptionPKCS7Padding;
}
}
}

You should use RNCryptor, it's high level encryption opensource api around CommonCrypto, and high level encryption API's are the best practice for cryptography these days, because it's easy for experts to make mistakes in the implementations using crypto primatives, and there are alot of side channel attacks out there that take advantage of those mistakes.
For example, you code says /* initialization vector (optional) * / 100% not true, thus you've totally crippled AES-CBC, and that's just the most obvious issue.
In your case RNCryptor is ideal, I'd strongly suggest you don't roll your own implementation.

Related

AES Encryption is not working (Xcode)

I'm trying to encrypt string in Xcode to PHP with AES128 method by using following code:
Xcode
- (NSData*)AES256EncryptWithKey:(NSString*)key {
char keyPtr[kCCKeySizeAES256];
[key cStringUsingEncoding:NSASCIIStringEncoding];
NSString *iv = #"fdsfds85435nfdfs";
char ivPtr[kCCKeySizeAES128];
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSASCIIStringEncoding];
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void* buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, NULL,
keyPtr, kCCKeySizeAES256,
ivPtr,
[self bytes], dataLength,
buffer, bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess)
{
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
But when I run above coding, following result is not what I expect to be:
NSString *key = #"89432hjfsd891787";
NSData *plaintext = [[#"aaa0000000000000" dataUsingEncoding:NSASCIIStringEncoding] AES256EncryptWithKey: key];
NSString *mystring = [[NSString alloc] initWithData:plaintext encoding:NSASCIIStringEncoding];
NSLog(#"mystring %#", mystring);
OUTPUT is
uçó)â½S/èRÅ
What I want it something like that.
m9FNGM9IiwibWFjIjoiNmJkYzNmZTA5
Note: due to a coding error the keyPtr is not set to the key value, it becomes the value of the uninitialized memory.
You get "uçó)â½S/èRÅ" because you try to create a string from the data and there are a couple reasons this will not work.
Many data bytes do not map to printable ASCII characters or to any unicode character.
A data byte can be 0 (1 in 256 bytes on average will be 0x00) and that is a terminator for a "C" string so the string will be short.
There are two general conventions for encoding data into a string representation:
Hex-ascii where each data byte is encoded into two characters 0-9a-f.
Base64 where each 3 data data bytes are encoded into 4 ASCII characters.
You are probably looking for Base64 encoding:
NSString *mystring = [plaintext base64EncodedStringWithOptions:0];
Other errors in the code:
[key cStringUsingEncoding:NSASCIIStringEncoding]; The string may not be ASCII, better to use NSUTF8StringEncoding. Note that the output is not captured.
The keyPtr is never set to the key value, see above.
The key size id specified as 256-bits (32-bytes) but the key is 16 characters, use a key size that matched the key.
char ivPtr[kCCKeySizeAES128], an iv size is the block size not the key size: kCCBlockSizeAES128.
Looks like you want a Base64 encoded string.
NSString *mystring = [plaintext base64EncodedStringWithOptions:0];

Changing ciphertext upon identical encryption (IV) Cocoa?

I was reading a tutorial on how to salt a key to make your encryption secure, but couldn't make much of it. I don't know a lot about cryptography, and need some help. I am using commoncrypto to encrypt files, and am done, except for the fact that it isn't secure... The ciphertext must not be the same when the user encrypts the same exact file with the same exact key twice.
This is what I have:
- (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)
NSLog(#"You are encrypting something...");
// 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;
}
If someone can help me out, and show me exactly how I would implement salt, that would be great! Thanks again!
First, what you are looking for here is called an initialization vector or IV. Salts are used with hashes, not ciphers. Note that both IVs and salts are specific examples of a nonce.
Now that we have terminology out of the way, what you'll want to do is use a different cipher mode. Currently you're using what's known as ECB - "electronic code book". As you have noted, it has the disadvantage that encrypting the same plaintext twice results in the same ciphertext, making it possible to reverse if the attacker can guess a potential plaintext.
There are a number of alternate cipher modes that fix this - one of the most popular ones is CBC - "cipher block chaining". Essentially, you insert a random block (the IV) at the start; then for each block, XOR the previous ciphertext block (the IV, for the first block) with the plaintext block before passing it through the cipher.

How to convert an NSData into an NSString Hex string?

When I call -description on an NSData object, I see a pretty Hex string of the NSData object's bytes like:
<f6e7cd28 0fc5b5d4 88f8394b af216506 bc1bba86 4d5b483d>
I'd like to get this representation of the data (minus the lt/gt quotes) into an in-memory NSString so I can work with it.. I'd prefer not to call -[NSData description] and then just trim the lt/gt quotes (because I assume that is not a guaranteed aspect of NSData's public interface and is subject change in the future).
What's the simplest way to get this representation of an NSData object into an NSString object (other than calling -description)?
Keep in mind that any String(format: ...) solution will be terribly slow (for large data)
NSData *data = ...;
NSUInteger capacity = data.length * 2;
NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity];
const unsigned char *buf = data.bytes;
NSInteger i;
for (i=0; i<data.length; ++i) {
[sbuf appendFormat:#"%02X", (NSUInteger)buf[i]];
}
If you need something more performant try this:
static inline char itoh(int i) {
if (i > 9) return 'A' + (i - 10);
return '0' + i;
}
NSString * NSDataToHex(NSData *data) {
NSUInteger i, len;
unsigned char *buf, *bytes;
len = data.length;
bytes = (unsigned char*)data.bytes;
buf = malloc(len*2);
for (i=0; i<len; i++) {
buf[i*2] = itoh((bytes[i] >> 4) & 0xF);
buf[i*2+1] = itoh(bytes[i] & 0xF);
}
return [[NSString alloc] initWithBytesNoCopy:buf
length:len*2
encoding:NSASCIIStringEncoding
freeWhenDone:YES];
}
Swift version
private extension Data {
var hexadecimalString: String {
let charA: UInt8 = 0x61
let char0: UInt8 = 0x30
func byteToChar(_ b: UInt8) -> Character {
Character(UnicodeScalar(b > 9 ? charA + b - 10 : char0 + b))
}
let hexChars = flatMap {[
byteToChar(($0 >> 4) & 0xF),
byteToChar($0 & 0xF)
]}
return String(hexChars)
}
}
I agree on the solution not to call description which is to be reserved for debugging, so good point and good question :)
The easiest solution is to loop thru the bytes of the NSData and construct the NSString from it. Use [yourData bytes] to access the bytes, and build the string into an NSMutableString.
Here is an example by implementing this using a category of NSData
#interface NSData(Hex)
-(NSString*)hexRepresentationWithSpaces_AS:(BOOL)spaces;
#end
#implementation NSData(Hex)
-(NSString*)hexRepresentationWithSpaces_AS:(BOOL)spaces
{
const unsigned char* bytes = (const unsigned char*)[self bytes];
NSUInteger nbBytes = [self length];
//If spaces is true, insert a space every this many input bytes (twice this many output characters).
static const NSUInteger spaceEveryThisManyBytes = 4UL;
//If spaces is true, insert a line-break instead of a space every this many spaces.
static const NSUInteger lineBreakEveryThisManySpaces = 4UL;
const NSUInteger lineBreakEveryThisManyBytes = spaceEveryThisManyBytes * lineBreakEveryThisManySpaces;
NSUInteger strLen = 2*nbBytes + (spaces ? nbBytes/spaceEveryThisManyBytes : 0);
NSMutableString* hex = [[NSMutableString alloc] initWithCapacity:strLen];
for(NSUInteger i=0; i<nbBytes; ) {
[hex appendFormat:#"%02X", bytes[i]];
//We need to increment here so that the every-n-bytes computations are right.
++i;
if (spaces) {
if (i % lineBreakEveryThisManyBytes == 0) [hex appendString:#"\n"];
else if (i % spaceEveryThisManyBytes == 0) [hex appendString:#" "];
}
}
return [hex autorelease];
}
#end
Usage:
NSData* data = ...
NSString* hex = [data hexRepresentationWithSpaces_AS:YES];
Just wanted to add that #PassKits's method can be written very elegantly using Swift 3 since Data now is a collection.
extension Data {
var hex: String {
var hexString = ""
for byte in self {
hexString += String(format: "%02X", byte)
}
return hexString
}
}
Or ...
extension Data {
var hex: String {
return self.map { b in String(format: "%02X", b) }.joined()
}
}
Or even ...
extension Data {
var hex: String {
return self.reduce("") { string, byte in
string + String(format: "%02X", byte)
}
}
}
I liked #Erik_Aigner's answer the best. I just refactored it a bit:
NSData *data = [NSMutableData dataWithBytes:"acani" length:5];
NSUInteger dataLength = [data length];
NSMutableString *string = [NSMutableString stringWithCapacity:dataLength*2];
const unsigned char *dataBytes = [data bytes];
for (NSInteger idx = 0; idx < dataLength; ++idx) {
[string appendFormat:#"%02x", dataBytes[idx]];
}
In Swift you can create an extension.
extension NSData {
func toHexString() -> String {
var hexString: String = ""
let dataBytes = UnsafePointer<CUnsignedChar>(self.bytes)
for (var i: Int=0; i<self.length; ++i) {
hexString += String(format: "%02X", dataBytes[i])
}
return hexString
}
}
Then you can simply use:
let keyData: NSData = NSData(bytes: [0x00, 0xFF], length: 2)
let hexString = keyData.toHexString()
println("\(hexString)") // Outputs 00FF
Sadly there's no built-in way to produce hex from an NSData, but it's pretty easy to do yourself. The simple way is to just pass successive bytes into sprintf("%02x") and accumulate those into an NSMutableString. A faster way would be to build a lookup table that maps 4 bits into a hex character, and then pass successive nybbles into that table.
While it may not be the most efficient way to do it, if you're doing this for debugging, SSCrypto has a category on NSData which contains two methods to do this (one for creating an NSString of the raw byte values, and one which shows a prettier representation of it).
http://www.septicus.com/SSCrypto/trunk/SSCrypto.m
Seeing there is a Swift 1.2 snippet in the comments, here's the Swift 2 version since C style for loops are deprecated now.
Gist with MIT license and two simple unit tests if you care.
Here's the code for your convenience:
import Foundation
extension NSData {
var hexString: String {
let pointer = UnsafePointer<UInt8>(bytes)
let array = getByteArray(pointer)
return array.reduce("") { (result, byte) -> String in
result.stringByAppendingString(String(format: "%02x", byte))
}
}
private func getByteArray(pointer: UnsafePointer<UInt8>) -> [UInt8] {
let buffer = UnsafeBufferPointer<UInt8>(start: pointer, count: length)
return [UInt8](buffer)
}
}
Assuming you have already set:
NSData *myData = ...;
Simple solution:
NSString *strData = [[NSString alloc]initWithData:myData encoding:NSUTF8StringEncoding];
NSLog(#"%#",strData);

Cocoa AES Encryption NSData and Bytearrays

I'm using the following code to encrypt files in cocoa:
- (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;
}
And wrote this for the connection to the file:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"foo" ofType:#"rtf"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSString *key = [withFileKey stringValue];
NSString *newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *encrypted = [newStr AES256EncryptWithKey:key];
NSLog(#"File encryption:%#", encrypted);
[filePathName setStringValue:filePath];
if (!data) {
NSLog(#"Unable to read file");
}
Basically what I did was first of all get the filepath of the file the user wants. Then convert the data in the file to a string. Then encrypt that string with the AES256EncryptWithKey: method. However, when I decrypt a plain text file for example, it returns a bunch of garbage like fonts and all that stuff, then the few lines I wrote. Something like this:
\ansicpg1252\cocoartf1138\cocoasubrtf100
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Menlo-Bold;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
\f0\fs24 \cf0 Hello my name is bobby bob\
\
\pard\tx560\pardeftab560\pardirnatural
\f1\b\fs22 \cf0 \CocoaLigature0 YAY!\
and I am awesome!!}
Shouldn't I be taking the data and then encrypting that (conversion to bytes), then convert the encrypted data and convert it to a string to display? I tried something like that but it didn't work. :(
Something like:
NSData *encryptedData = [data AES256EncryptWithKey:yourkey];
And then:
NSString *convertData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
?
Your help is greatly appreciated. Thanks!
Your code appears hard-coded to load foo.rtf. This looks like an RTF file. Where is the "plain text file" you're talking about?
EDIT We had a lot of discussion on this, so I wrote up a blog post about how to correctly use CCCrypt().

Any cocoa source code for AES encryption decryption?

I am searching for some cocoa code on AES encryption and I did some google search for it. I found this very useful link -
http://iphonedevelopment.blogspot.com/2009/02/strong-encryption-for-cocoa-cocoa-touch.html. So I tried it but it did not work for me.
Can anyone suggest me some useful link or source code which can help me to implement it in my sample application.
I use a simple category on NSData that uses the built-in CommonCrypto framework to do AES 256-bit encryption. I use this on the Mac but it should work OK on iPhone too:
#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
AES128 encryption is available on the iPhone in the CommonCrypto framework. The relevant functions are in the CommonCryptor.h header.
You can create a cryptor like so:
// Assume key and keylength exist
CCCryptorRef cryptor;
if(kCCSuccess != CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, 0, key, keyLength, NULL, &cryptor))
; //handle error
// Repeatedly call CCCryptorUpdate to encrypt the data
CCCryptorRelease(cryptor);
It seems from the question and the link that you are looking for example implementations of AES. I would not recommend this- use Apple's implementation!
It looks like http://pastie.org/297563.txt might help you also, but I haven't tested it.
All examples I found didn't work for me, so I changed the solution above. This one works for me and uses the Google-Lib for Base64 stuff:
+ (NSData *)AES256DecryptWithKey:(NSString *)key data:(NSData*)data encryptOrDecrypt:(CCOperation)encryptOrDecrypt {
// '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];
if (encryptOrDecrypt == kCCDecrypt)
{
data = [GTMBase64 decodeData:data];
}
NSUInteger dataLength = [data 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(encryptOrDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keyPtr,
kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[data bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted);
if (cryptStatus != kCCSuccess){
NSLog(#"ERROR WITH FILE ENCRYPTION / DECRYPTION");
return nil;
}
NSData *result;
if (encryptOrDecrypt == kCCDecrypt)
{
result = [NSData dataWithBytes:(const void *)buffer length:(NSUInteger)numBytesDecrypted];
}
else
{
NSData *myData = [NSData dataWithBytes:(const void *)buffer length:(NSUInteger)numBytesDecrypted];
result = [GTMBase64 encodeData:myData];
}
free(buffer); //free the buffer;
return result;
}
thanks for the great category extension. One thing that I've discovered - when you use CCCrypt with an algorithm stronger then 64 bit, you need to be compliant to the BIS export regulations. See the iTunes Connect FAQ for more details. Even if you use Apple's inbuild crypto api you need to get approval from the BIS.
There was a discussion on sf about this topic before (in context of ssl usage):
Using SSL in an iPhone App - Export Compliance
Best Regards
Chris

Resources