I am experimenting with OpenSSL under Objective C. I am trying to write SMIME asymmetric key encrypted data. I have no problem with opening a file, encrypting it and writing it to disk. I can encrypt a single memory buffer. What I want to do is implement a sort of scatter-gather approach with multiple memory buffers fed into the same encrypting BIO and written to disk serially.
My code is simple. I set up a cipher and get the public key/cert with PEM_read_bio_X509_AUX(). I push it on a STACK_OF X509s, certs, with sk_X509_push(certs,...). I open my output file with writeBIO=BIO_new_file(). I create a memory BIO with readBIO=BIO_new_mem_buf(buf,len); and encrypt it with p7=PKCS_encrypt(certs,readBIO,...);. Finally I write it to the output with i2d_PKCS7_bio(writeBIO,p7); This works fine for just one memory buffer. If I try to pass multiple buffers in a loop, only the last is output. I have tried freeing and re-allocating the readBIO abd p7 structured in between calls to BIO_new_mem_buf but this does not work.
Any ideas?
UPDATE - I have a solution but not one I'm overjoyed about:
I have a wrapper for OpenSSL called AETPublicKeyWrapper
#interface AETPublicKeyWrapper : NSObject
{
#private
BIO *readBIO;
BIO *writeBIO;
STACK_OF(X509) *certs;
const char *rmode,*wmode;
PKCS7 *p7;
EVP_PKEY *privateKey;
const EVP_CIPHER *cipher;
int informat,outformat,flags;
}
...
-(BOOL)openInputFile:(const char*)fname;
-(BOOL)openOutputFile:(const char*)fname;
-(BOOL)openInputBuffer;
-(int)writeToInputBuffer:(const void*)buf length:(unsigned long long)len;
-(BOOL)loadPublicKeyCert:(const char*)certFileName;
...
-(BOOL)encryptInput;
-(BOOL)writeEncryptedOutput;
-(BOOL)writeDecryptedOutput;
...
This encapsulates various OpenSSL API calls. -(BOOL)openOutputFile: for example is just
-(BOOL)openOutputFile:(const char*)fname
{
if(!(writeBIO=BIO_new_file((char*)fname,"wb")))
return NO;
return YES;
}
My encryption routine for multiple buffers into the same output looks like this:
AETPublicKeyWrapper *owrapper=[[AETPublicKeyWrapper alloc] init];
[owrapper loadPublicKeyCert:[keyPath cStringUsingEncoding:NSASCIIStringEncoding]]
[owrapper openOutputFile:[saveAsPath cStringUsingEncoding:NSASCIIStringEncoding]]
[owrapper openInputBuffer]
/* fileData is a header */
[owrapper writeToInputBuffer:[fileData bytes] length:[fileData length]]
/* dirArray is an array of buffers */
for(NSData *itemData in dirArray)
[owrapper writeToInputBuffer:[itemData bytes] length:[itemData length]];
[owrapper encryptInput]
[owrapper writeEncryptedOutput]
[owrapper flush]
[owrapper release]
The various methods are (most error handling omitted; the casts are to shut the compiler up)
-(BOOL)loadPublicKeyCert:(const char*)certFileName
{
X509 *x=NULL;
BIO *newCert;
certs=sk_X509_new_null();
newCert=BIO_new(BIO_s_file());
BIO_read_filename(newCert,certFileName);
x=PEM_read_bio_X509_AUX(newCert,NULL,NULL,NULL);
sk_X509_push(certs,x);
return YES;
}
-(BOOL)openInputBuffer
{
if(!(readBIO=BIO_new(BIO_s_mem())))
return NO;
return YES;
}
-(int)writeToInputBuffer:(const void*)buf length:(unsigned long long)len
{
return BIO_write(readBIO,(void*)buf,(int)len);
}
-(BOOL)encryptInput
{
if(!(p7=PKCS7_encrypt(certs,readBIO,cipher,flags)))
return NO;
return YES;
}
-(BOOL)writeEncryptedOutput
{
if(i2d_PKCS7_bio(writeBIO,p7)<=0)
return NO;
return YES;
}
This works, but my concern is that the input buffer could get arbitrarily large, so I would prefer to be able to write to the output and have it flush through with each write rather than accumulating and doing the whole encrypt/write thing at the end.
Related
I have been working on this for a while, but can not find a way to tackle the problem. Hopefully one of you can tell me what I am missing.
I am using NSURLConnection to download base64 encoded data containing AES128 encrypted data. What I have is the key, see code, and the knowledge that the first 16 characters of the encrypted data is the IV. What I want is to decode the data and then decrypt it using the key and iv extracted. This is what I have so far:
- (void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(#"Succeeded! Downloaded %d bytes of data", downloadData.length);
NSData *decoded_EncryptedData = [downloadData base64EncodedDataWithOptions:0];
NSString *decoded_EncryptedString = [[NSString alloc] initWithData: decoded_EncryptedData encoding:NSUTF8StringEncoding];
const void *key = #"0000000000000000000000000000000"; // key of length 32 char -> i know standard format for AES128 encryption is 16, maybe this requires 256 AES decryption
const void *iv = (__bridge const void *)([decoded_EncryptedString substringWithRange:NSMakeRange(0,16)]);
NSString *encryptedString = [decoded_EncryptedString substringWithRange:NSMakeRange(16, decoded_EncryptedString.length-16)];
// Now I have no idea what needs to happen, but from online research I found it should be something like this:
NSData encryptedData = [encryptedString dataUsingEncoding:NSUTF8StringEncoding]; // Writing it back into a data file
// Find size of returned data
size_t Size = encryptedData.length + kCCBlockSizeAES128;
// Initialise returned data
NSMutableData *decryptedData = [NSMutableData dataWithLength:Size];
// allocate variable to numBytesDecrypted
size_t numBytesDecrypted;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, KCCKeySizeAES128, iv,
[encryptedData bytes], [encryptedData length], [decryptedData bytes], [decryptedData length],
&numBytesDecrypted);
// Now I test whether the decryption process was successful:
if (cryptStatus == kCCSuccess) {
NSLog(#"Successfully decrypted);
NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding: NSUTF8StringEncoding];
}
}
The above code does display Successfully decrypted, however the string return null and size 0. Could someone please help me solve this? I would be so grateful.
Kind regards,
Lennaert
You have many problems.
You really need to know if the encryption is AES128 or AES256.
Encryption is data based, not string based. The conversion to a string decoded_EncryptedString is incorrect not should not be done.
The key is an issue, using a string is generally a bad idea, it is expected to be data bytes. Possibly the key is specified in hex so 32 hex characters would be 128 bits. If so conversion to data will be required.
'iv' and encryptedString are strings but they should be data, this a result of 2 above.
The key is not passed to CCCrypt.
Padding is generally used since the data is rarely exactly a block size in length, you probably need to specify PKCS7 padding to CCCrypt. You need to know if padding was used and if so was it PKCS7, php for example uses non-standard padding.
If PKCS7 padding is used the result must be trimmed to the length based on the variable numBytesDecrypted. If some other padding is used that must be trimmed.
If you want more help please supply test data and the result.
Finally, try and check back more frequently.
I love blocks, and they are very cool.
However, I find that blocks can clutter up my code and make it harder to read without folding all of them up inside Xcode (which I don't like doing).
I like splitting my code into logical methods (selectors) to keep it easier to read, but it appears (on the surface) that this isn't readily possible with frameworks like dispatch, AFNetworking, and several others.
I also don't care for the delegate approach, as that means I cannot name my methods as I would like to, instead relying on what other people think I need.
So, without writing a bunch of glue code like this:
-(void) reloadData {
...
[[EventsManager instance] data:YES async:^(NSArray *events) { [self processEvents:events] }];
...
}
I could instead do something like this:
-(void) reloadData {
...
[[EventsManager instance] data:YES async:createBlock(self, #selector(processEvents:))];
...
}
Which is easier to read (to me).
With the power that we have with objective-c, and it's runtime, this should be possible, no? I haven't seen anything like this out there, though.
I liked your answer from an academic standpoint; +1 and, clearly, you learned something.
From a practical perspective, it seems like an awful lot of added fragility for very little reduction in typing while it also leads to some information loss at the call site.
The advantage of this is that it is exactly explicit:
-(void) reloadData {
...
[[EventsManager instance] data:YES async:^(NSArray *events) { [self processEvents:events] }];
...
}
Reading that, one sees that the asynchronous callback block is required to process the arguments and that the processEvents: method on self will be used to do the actual work.
The expression createBlock(self, #selector(processEvents:)) is a lossy representation of the same; it loses the explicit argumentation of the callback and the mapping between that argumentation and the method being invoked (I often see callback blocks like the above with multiple arguments where there is some lightweight logic and/or argument processing before invoking the method).
Note also that processing a non-varargs call site as a varargs when called is a violation of the C standard and will not work on certain ABIs with certain lists of arguments.
Yes, this is indeed possible, but this solution is ABI-specific (not guaranteed to work on all platforms), and makes extensive use of the information available at run-time about methods.
What we first must do is get information about the method we are wrapping with the block. This is done via NSMethodSignature, which contains information such as:
Number of arguments
Size (in bytes) of each argument
Size of return type
This allows us to wrap (almost) any method with no specific code for that method, thus creating a re-usable function.
Secondly, we need a way to safely dispatch method calls at run-time. We do this via NSInvocation, which grants us the ability to create a dynamic, and safe, method call at run-time.
Thirdly, we need to have a block that can take any number of arguments passed in, and the dispatch that. This is done via C's va_list APIs, and should work for 99% of methods.
Finally, we need to get the return value, and be able to return that from our block. This is the part of the entire operation that is possible to not work, because of weirdness with returning structs and such with the Objective-C runtime.
However, as long as you keep to primitive types and Objective-C objects, this code should work great for you.
A couple of things to note about this implementation:
It is reliant upon undefined behavior with casting of block & function types, however, because of the calling conventions of iOS and Mac, this should not pose any issues (unless your method has a different return type than what the block expects).
It also relies upon undefined behavior with the result of calling va_arg with a type that may not be what is passed - however, since the types are of the same size, this should never be an issue.
Without any further ado, here is an example of the code, followed by the implementation:
#interface MyObj : NSObject
-(void) doSomething;
#end
#implementation MyObj
-(void) doSomething
{
NSLog(#"This is me, doing something! %p", self);
}
-(id) doSomethingWithArgs:(long) arg :(short) arg2{
return [NSString stringWithFormat:#"%ld %d", arg, arg2];
}
#end
int main() {
// try out our selector wrapping
MyObj *obj = [MyObj new];
id (^asBlock)(long, short) = createBlock(obj, #selector(doSomethingWithArgs::));
NSLog(#"%#", asBlock(123456789, 456));
}
/* WARNING, ABI SPECIFIC, BLAH BLAH BLAH NOT PORTABLE! */
static inline void getArgFromListOfSize(va_list *args, void *first, size_t size, size_t align, void *dst, BOOL isFirst) {
// create a map of sizes to types
switch (size) {
// varargs are weird, and are aligned to 32 bit boundaries. We still only copy the size needed, though.
// these cases should cover all 32 bit pointers (iOS), boolean values, and floats too.
case sizeof(uint8_t): {
uint8_t tmp = isFirst ? (uint32_t) first : va_arg(*args, uint32_t);
memcpy(dst, &tmp, size);
break;
}
case sizeof(uint16_t): {
uint16_t tmp = isFirst ? (uint32_t) first : va_arg(*args, uint32_t);
memcpy(dst, &tmp, size);
break;
}
case sizeof(uint32_t): {
uint32_t tmp = isFirst ? (uint32_t) first : va_arg(*args, uint32_t);
memcpy(dst, &tmp, size);
break;
}
// this should cover 64 bit pointers (Mac), and longs, and doubles
case sizeof(uint64_t): {
uint64_t tmp = isFirst ? (uint64_t) first : va_arg(*args, uint64_t);
memcpy(dst, &tmp, size);
break;
}
/* This has to be commented out to work on iOS (as CGSizes are 64 bits)
// common 'other' types (covers CGSize, CGPoint)
case sizeof(CGPoint): {
CGPoint tmp = isFirst ? *(CGPoint *) &first : va_arg(*args, CGPoint);
memcpy(dst, &tmp, size);
break;
}
*/
// CGRects are fairly common on iOS, so we'll include those as well
case sizeof(CGRect): {
CGRect tmp = isFirst ? *(CGRect *) &first : va_arg(*args, CGRect);
memcpy(dst, &tmp, size);
break;
}
default: {
fprintf(stderr, "WARNING! Could not bind parameter of size %zu, unkown type! Going to have problems down the road!", size);
break;
}
}
}
id createBlock(id self, SEL _cmd) {
NSMethodSignature *methodSig = [self methodSignatureForSelector:_cmd];
if (methodSig == nil)
return nil;
return ^(void *arg, ...) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setTarget:self];
[invocation setSelector:_cmd];
NSUInteger argc = [methodSig numberOfArguments];
va_list args;
va_start(args, arg);
for (int argi = 2; argi < argc; argi++) {
const char *type = [methodSig getArgumentTypeAtIndex:argi];
NSUInteger size;
NSUInteger align;
// get the size
NSGetSizeAndAlignment(type, &size, &align);
// find the right type
void *argument = alloca(size);
getArgFromListOfSize(&args, arg, size, align, argument, argi == 2);
[invocation setArgument:argument atIndex:argi];
}
va_end(args);
[invocation invoke];
// get the return value
if (methodSig.methodReturnLength != 0) {
void *retVal = alloca(methodSig.methodReturnLength);
[invocation getReturnValue:retVal];
return *((void **) retVal);
}
return nil;
};
}
Let me know if you have any issues with this implementation!
I'm trying to learn objective-c. I'm trying to parse a binary file doing the following (simplified):
#interface DatFile : NSObject {
NSData* _data;
}
-(id)initWithFilePath:(NSString *)filePath;
-(void) readFile;
-(void) auxiliaryMethod;
#implementation DatFile
- (id) initWithFilePath:(NSString *)filePath {
if ( self = [super init] ) {
_data = [NSData dataWithContentsOfFile:filePath];
}
return self;
}
-(void) readFile {
int header;
[_data getBytes: &header range: NSMakeRange(0, 4)];
NSLog(#"header: %u", header);
short key;
[_data getBytes: &key range: NSMakeRange(4, 2)];
NSLog(#"key: %u", key);
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
[self auxiliaryMethod];
}
-(void) auxiliaryMethod {
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
}
My problem is that the code inside the auxiliaryMethod does not compute the same value computed by readFile method. Since the _data object is the same, why the method are computing different values? And, as you can see, the logic inside the auxiliaryMethod is just a copy of the other one...
In other languages (java) I usually separate some logic in smaller methods in order to make the code mode readable/maintainable. This is why I'm trying to mimic it with ObjC.
Of course that probably missing something, but after some hours, I gave up. I don't see where is my mistake. Probably I should erase my project and start it again...
TIA,
Bob
%u is for printing a 32 bit unsigned value. A short is 16 bits. Therefore, you are printing the parsed value plus some stack garbage.
Try %hu; the h modifies %u to print a short value.
If that doesn't work:
make sure your data objects are the same between the two methods
(don't see how they can't be... but...)
show the actual values printed
decode into an unsigned long that is explicitly initialized to 0 before decoding
With this kind of bug, it is pretty much guaranteed it is either that you aren't dealing with the input you think you are or you are dealing with some C-ism related to data types and the implied conversions in this kind of code.
I.e. assuming _data is consistent, then it indicates that the code you think is working is not actually working, but only appearing to by coincidence.
The problem is that you're passing a range of size 1 to getBytes:range:, but a short is 2 bytes in size. So getBytes:range: is only setting one of value's bytes, and the other contains random garbage.
If you really only want to get one byte from the data, change the type of value to int8_t. If you want to get two bytes, pass 2 as the second argument of NSMakeRange (and I recommend also changing the type of value to int16_t).
I am writing a text-editor and I would need to store a few pieces of information (generally just a few strings; the storage needn't be particularly durable) with each file the app saves (without that being part of the text-file as other apps might read it and the info is only specific to my app).
How would I go about this?
More info: I have a NSDocument set up and I would like to simply store a NSString instance variable as a per file meta-datum. Based on the answers below I've come up with this, which is currently buggy and causes the program to crash on startup:
#import <sys/xattr.h>
#interface MyDocument : NSDocument {
NSString *metadatum;
}
#implementation MyDocument
- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)type error:(NSError **)err
{
BOOL output = [super writeToURL:url ofType:type error:err];
if(!setxattr([[url path] cStringUsingEncoding:NSUTF8StringEncoding],
"eu.gampleman.xattrs.style",
[metadatum cStringUsingEncoding:NSUTF8StringEncoding],
sizeof(char) * [styleName length], 0, 0))
{
NSLog(#"Write failure");
}
return output;
}
- (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)type error:(NSError **)err {
char *output;
ssize_t bytes = getxattr([[url path] cStringUsingEncoding:NSUTF8StringEncoding],
"eu.gampleman.xattrs.style", &output, 1024, 0, 0);
if (bytes > 0) {
metadatum = [[NSString alloc] initWithBytes:output length:bytes
encoding:NSUTF8StringEncoding]; // <- crashes here with "EXC_BAD_ACCESS"
}
return [super readFromURL:url ofType:type error: err];
}
// ...
// fairly standard -dataOfType:error: and
// -readFromData:ofType:error: implementations
PS: If your answer is really good (with sample code, etc.), I will award a 100rep bounty.
Use extended attributes. See setxattr().
Here's a sample call to write a string:
NSData* encodedString = [theString dataUsingEncoding:NSUTF8StringEncoding];
int rc = setxattr("/path/to/your/file", "com.yourcompany.yourapp.yourattributename", [encodedString bytes], [encodedString length], 0, 0);
if (rc)
/* handle error */;
To read a string:
ssize_t len = getxattr("/path/to/your/file", "com.yourcompany.yourapp.yourattributename", NULL, 0, 0, 0);
if (len < 0)
/* handle error */;
NSMutableData* data = [NSMutableData dataWithLength:len];
len = getxattr("/path/to/your/file", "com.yourcompany.yourapp.yourattributename", [data mutableBytes], len, 0, 0);
NSString* string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
PS: Don't you have to set the bounty on the question before it's answered?
There are several places to store this kind of information on Mac. The most simple, of course, is to store it in your own separate metadata database. Of course there are challenges if the file moves. Since 10.6, however, you can use Bookmarks to address this problem. A Bookmark (see NSURL) allows you to keep a reference to a file even if it is moved (even across application restarts). Prior to 10.6 there was the Alias Manager, but it couldn't create new aliases; just read ones that Finder created.
The next common solution is to create metadata files. So if I have foo.txt, then you create a sibling .foo.txt.metadata to hold the extra info. Several trade-offs there if the files can be moved around.
Next is Spotlight, which can be used to attach arbitrary information to files. The actual tool here is xattr (see the man pages for setxattr and its relatives). These basically absorb several things that used to be done with Resource Forks (except xattr is supposed to just be metadata).
My app encrypts and decrypts (or it should) an NSString (the text to be encrypted / decrypted) with another NSString (the keyword) using aes 256-Bit Encryption. When I run my project and run the encrypt method, nothing gets encrypted the textfield just clears itself. Here is the code I have:
-(void)EncryptText {
//Declare Keyword and Text
NSString *plainText = DataBox.text;
NSString *keyword = Keyword.text;
//Convert NSString to NSData
NSData *plainData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
//Encrypt the Data
NSData *encryptedData = [plainData AESEncryptWithPassphrase:keyword];
//Convert the NSData back to NSString
NSString* cypherText = [[NSString alloc] initWithData:encryptedData encoding:NSUTF8StringEncoding];
//Place the encrypted sting inside the Data Box
NSLog(#"Cipher Text: %#", cypherText);
}
The header files can be downloaded by clicking this link: ZIP File containing AES Implementation
I have been told that I need to use Base-64 encoding of my string to get any result. If this is true, then how do I do it?
I have also been told that encryption changed in iOS 5, and my app is an iOS 5+ ONLY app. If this is true, then what do I have to do to make this encryption work on iOS 5 or where can I find another AES 256-bit implementation that will work on NSString.
Why doesn't this code produce a result?
EDIT: The links below refer to an older implementation. The latest version is called RNCryptor.
Your code doesn't use iOS's built-in AES implementation. It has its own custom implementation. AESEncryptWithPassphrase: also incorrectly generates the key, throwing away most of the entropy in the passphrase.
On iOS, you should be using the CCCrypt*() functions for AES. You should also make sure that you understand what is happening in your encryption and decryption routines. It is very easy to write encryption code that looks correct (in that you cannot read the output by inspection), but is extremely insecure.
See Properly encrypting with AES with CommonCrypto for an explanation of the problems with the above implementation, and how to properly use AES on iOS. Note that iOS 5 now has CCKeyDerivationPBKDF available.
There is no requirement to Base-64 encode your string prior to encryption. Base-64 encoding is used in cases where you need to convert binary data into a form that can be easily sent over email or other places where control characters would be a problem. It converts 8-bit binary data in 7-bit ASCII data. That's not necessary or useful here.
EDIT: It is critical that you carefully read the explanation of how to use this code. It is dangerous to simply cut and paste security code and hope it works. That said, the full source to RNCryptManager is available as part of the Chapter 11 example code for iOS 5 Programming Pushing the Limits and may be helpful [EDIT: This is old code; I recommend RNCryptor now, linked at the top of the answer]. The book (which should be available next week despite what the site says) includes a much longer discussion of how to use this code, including how to improve performance and deal with very large datasets.
NSData with category just fine for AES encryption, I didnt check zip file but this should work for you;
#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
Use it wrapper functions like ;
- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}
- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
encoding:NSUTF8StringEncoding] autorelease];
}