Symmetric Encryption with the iPhone SDK and Security.framework
Submitted by greg on Sat, 01/17/2009 - 17:27
Tagged:
As discussed here, this is a working example of using Security.framework and symmetric encryption. It uses AES128 since some of the docs suggest that the iPhone has optimized hardware for this algorithm and key size. This one is mostly based on the examples from Apple's documentation and samples. Any and all comments and criticisms are welcome. It's been a long time since I've worked in C (and even then I didn't enjoy it!). So if I could be doing anything better, let me know! If you're looking for asymmetric encryption, look here.
- (void)testSymmetricEncryption {
NSLog(@"testing symmetric encrypt/decrypt ...");
NSString *_key = @"this is my secret key ... change me!";
NSString *_secret = @"this is some secret text";
NSData *_secretData = [_secret dataUsingEncoding:NSASCIIStringEncoding];
CCOptions padding = kCCOptionPKCS7Padding;
NSData *encryptedData = [self encrypt:_secretData key:[self md5data:_key] padding:&padding];
NSLog(@"encrypted data: %@", encryptedData);
NSData *data = [self decrypt:encryptedData key:[self md5data:_key] padding:&padding];
NSLog(@"decrypted data: %@", data);
NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(@"decrypted string: %@", str);
NSLog(@"test finished.");
}
- (NSData *)encrypt:(NSData *)plainText key:(NSData *)aSymmetricKey padding:(CCOptions *)pkcs7 {
return [self doCipher:plainText key:aSymmetricKey context:kCCEncrypt padding:pkcs7];
}
- (NSData *)decrypt:(NSData *)plainText key:(NSData *)aSymmetricKey padding:(CCOptions *)pkcs7 {
return [self doCipher:plainText key:aSymmetricKey context:kCCDecrypt padding:pkcs7];
}
- (NSData *)doCipher:(NSData *)plainText key:(NSData *)aSymmetricKey
context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7 {
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[kChosenCipherBlockSize];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
NSLog(@"doCipher: plaintext: %@", plainText);
NSLog(@"doCipher: key length: %d", [aSymmetricKey length]);
LOGGING_FACILITY(plainText != nil, @"PlainText object cannot be nil." );
LOGGING_FACILITY(aSymmetricKey != nil, @"Symmetric key object cannot be nil." );
LOGGING_FACILITY(pkcs7 != NULL, @"CCOptions * pkcs7 cannot be NULL." );
LOGGING_FACILITY([aSymmetricKey length] == kChosenCipherKeySize, @"Disjoint choices for key size." );
plainTextBufferSize = [plainText length];
LOGGING_FACILITY(plainTextBufferSize > 0, @"Empty plaintext passed in." );
NSLog(@"pkcs7: %d", *pkcs7);
// We don't want to toss padding on if we don't need to
if(encryptOrDecrypt == kCCEncrypt) {
if(*pkcs7 != kCCOptionECBMode) {
if((plainTextBufferSize % kChosenCipherBlockSize) == 0) {
*pkcs7 = 0x0000;
} else {
*pkcs7 = kCCOptionPKCS7Padding;
}
}
} else if(encryptOrDecrypt != kCCDecrypt) {
LOGGING_FACILITY1( 0, @"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7 );
}
// Create and Initialize the crypto reference.
ccStatus = CCCryptorCreate(encryptOrDecrypt,
kCCAlgorithmAES128,
*pkcs7,
(const void *)[aSymmetricKey bytes],
kChosenCipherKeySize,
(const void *)iv,
&thisEncipher
);
LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem creating the context, ccStatus == %d.", ccStatus );
// 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 *) [plainText bytes],
plainTextBufferSize,
ptr,
remainingBytes,
&movedBytes
);
LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem with CCCryptorUpdate, ccStatus == %d.", ccStatus );
// Handle book keeping.
ptr += movedBytes;
remainingBytes -= movedBytes;
totalBytesWritten += movedBytes;
/* From CommonCryptor.h:
@enum CCCryptorStatus
@abstract Return values from CommonCryptor operations.
@constant kCCSuccess Operation completed normally.
@constant kCCParamError Illegal parameter value.
@constant kCCBufferTooSmall Insufficent buffer provided for specified operation.
@constant kCCMemoryFailure Memory allocation failure.
@constant kCCAlignmentError Input size was not aligned properly.
@constant kCCDecodeError Input data did not decode or decrypt properly.
@constant kCCUnimplemented Function not implemented for the current algorithm.
enum {
kCCSuccess = 0,
kCCParamError = -4300,
kCCBufferTooSmall = -4301,
kCCMemoryFailure = -4302,
kCCAlignmentError = -4303,
kCCDecodeError = -4304,
kCCUnimplemented = -4305
};
typedef int32_t CCCryptorStatus;
*/
// Finalize everything to the output buffer.
ccStatus = CCCryptorFinal(thisEncipher,
ptr,
remainingBytes,
&movedBytes
);
totalBytesWritten += movedBytes;
if(thisEncipher) {
(void) CCCryptorRelease(thisEncipher);
thisEncipher = NULL;
}
LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem with encipherment ccStatus == %d", ccStatus );
if (ccStatus == kCCSuccess)
cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten];
else
cipherOrPlainText = nil;
if(bufferPtr) free(bufferPtr);
return cipherOrPlainText;
/*
Or the corresponding one-shot call:
ccStatus = CCCrypt( encryptOrDecrypt,
kCCAlgorithmAES128,
typeOfSymmetricOpts,
(const void *)[self getSymmetricKeyBytes],
kChosenCipherKeySize,
iv,
(const void *) [plainText bytes],
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes
);
*/
}
Bookmark/Search this post with

Comments
Pingback
[...] I’m trying to use this class for encryption, which i found in this site: http://greghaygood.com/2009/01/17/symmetric-encryption-with-the-iphone-s.... [...]
Pingback
[...] I’m trying to use this class for encryption, which i found in this site: http://greghaygood.com/2009/01/17/symmetric-encryption-with-the-iphone-s.... [...]
Pingback
[...] encryption using the iPhone SDK’s Security.framework. He provides links for examples of doing symmetric encryption and asymmetric encryption. Mobile Development, Security, iPhone [...]
where's md5data ? Am i
where's md5data ? Am i missing an import? already have commoncrypto reference.
Cheers man!
hey Greg, I am getting this
hey Greg, I am getting this warning after executing your code on the iPhone simulator - warning: no md5data method..
could you give the method definition for md5data in your code. My application is crashing as it is not able to locate the method.
And what should be the kChosenCipherKeySize ?
thanks
Soumya
Hi I have a question, What is
Hi
I have a question, What is this method [self md5data:] do?
Is there source code for that?
Thanks
Gonso
I didn't use this
I didn't use this anymore:
if((plainTextBufferSize % kChosenCipherBlockSize) == 0) {
*pkcs7 = 0x0000;
} else {
*pkcs7 = kCCOptionPKCS7Padding;
}
I had problems with some of my strings I did encrypt most of them worked, some of them I just couldn't decrypt well..
Ries
I use the above code, and for
I use the above code,
and for one reason or the other,
it works with 95% of my strings (about 3000 of them), but some of them de-crypt incorrectly with a error -4304.
Currently pretty much clueless why this happens!
Ries
"CCOptions undeclare" This is
"CCOptions undeclare" This is the error statement i am getting. Should i add any header file for CCOption .
This is very, very helpful,
This is very, very helpful, but I see your class fragment does not define -md5data:. Is it simply a hash of its argument?
Work well thanks. I can
Work well thanks. I can crypt/decryp some littles picutures but when I increase the classes doesn’t work no more. For example I can’t decrypt an JPG image of 96kB :(
any idea !?
Thanks
[...] conversion. I didn’t
[...] conversion. I didn’t write most of the code - thanks to Blue Beetle for the .Net code and Greg Haygood for the Objective [...]
Hey, thanks. The
Hey, thanks. The base64EncodingWithLineLength method works on the iPhone, but the platform I am sending it to is .Net and the built in AesCryptoServiceProvider doesn't read it. The keys each method generates are different:
hello/hello is:
e0PnmbTg/3cT3W+92CDw1Q== in .Net
yrKe5Z7p7MNqx9+CbBvNqQ== on iPhone
and
openssl enc -aes-128-cbc -a -in hello.txt -pass pass:hello
generates:
U2FsdGVkX19Xf63uRy1ySGNpXoIMSibOS1ZDRuvFFE8=
I asked for help here too: http://stackoverflow.com/questions/538435/aes-interoperability-between-n...
You can Base64-encode the
You can Base64-encode the NSData block, then decode on your server-side. There are a few classes floating around. I used this one: http://svn.cocoasourcecode.com/MGTwitterEngine/NSData+Base64.m
Hey Greg, Your code works
Hey Greg,
Your code works great, but I am having trouble with something. I want to take the encrypted NSData and output it to a string to send to a web service for decryption. However, the encrypted string looks like "0̵Uêd{TãϹÚOÖ.$4§@#ú÷" - it's not ASCII encoded. I have tried [[NSString alloc] initWithData:encryptedData encoding:NSASCIIStringEncoding] and [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding] but it doesn't work. Do you have any ideas?
Same issue here.
Same issue here.
Use the NSArchiver class,
Use the NSArchiver class, which doesn't care about whether the file is binary or text. So here's how I'm encrypting and persisting a mutable array:
- (BOOL) loadFileContents: (NSString *)passphrase { NSLog (@"loadFileContents(%@) ...", passphrase); if (passphrase == nil) { NSLog(@"passphrase is empty, so returning NO"); return NO; } NSFileManager *manager = [NSFileManager defaultManager]; if ([manager fileExistsAtPath: appDataFile]) { NSLog(@"token file exists ... loading data..."); CCOptions padding = kCCOptionPKCS7Padding; NSData *encryptedData = [[NSMutableData alloc] initWithContentsOfFile:appDataFile]; NSLog(@"Decrypting data using passphrase (%@) ...", passphrase); NSData *data = [self decrypt:encryptedData key:[self md5data:passphrase] padding:&padding]; [encryptedData release]; if (data == nil) { NSLog(@"data == nil"); return NO; } NSLog(@"Unarchiving token data ..."); NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; self.myPersistedObject = [[unarchiver decodeObjectForKey:keyDomainList] retain]; //self.publicTag = [[unarchiver decodeObjectForKey:keyDomainList] retain]; //self.myPersistedObject = [[unarchiver decodeObjectForKey:keyDomainList] retain]; [unarchiver finishDecoding]; [unarchiver release]; if (self.myPersistedObject == nil) { NSLog(@"loadFileContents: myPersistedObject == nil, so creating new one"); //Instantiate list NSMutableArray *localList = [[NSMutableArray alloc] init]; self.myPersistedObject = localList; [localList release]; } NSLog(@"loadFileContents: domain list: %@", self.myPersistedObject); } NSLog(@"loadFileContents: myPersistedObject = %@", self.myPersistedObject); NSLog (@"loadFileContents() finished."); return YES; } - (BOOL) saveFileContents { NSLog (@"saveFileContents() ..."); if (cachedPassphrase == nil) { NSLog(@"passphrase is empty, so not saving to file"); return NO; } NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [archiver encodeObject:self.myPersistedObject forKey:keyDomainList]; [archiver finishEncoding]; [archiver release]; CCOptions padding = kCCOptionPKCS7Padding; NSLog(@"Encrypting data using passphrase (%@) ...", cachedPassphrase); NSData *encryptedData = [self encrypt:data key:[self md5data:cachedPassphrase] padding:&padding]; [data release]; NSLog(@"Writing file to disk ..."); [encryptedData writeToFile:appDataFile atomically:YES]; NSLog (@"saveFileContents() finished."); return YES; }Hey Greg, thanks for posting
Hey Greg, thanks for posting the info about how to accomplish encryption in iPhone apps--what a great resource! I'm just starting to dip my toe into the "iphone encryption" waters and have a newbie question: do you think it's possible to use this technique to encrypt binary files?
Perhaps a worst-case-scenario solution would involve converting a binary file to a textual representation (e.g., hexadecimal ascii characters) and encrypting that? Of course I have no idea how you would convert the hex/ascii characters back to a binary file when you wanted to decrypt the file...
What do you think?
Thanks again,
clint
Post new comment