I'm trying to encrypt some data with RSA using the Security framework on iOS. I want to encrypt a simple base64-encoded string as follows:
NSData *data = [[NSData alloc] initWithBase64EncodedString:#"aGFsbG8=" options:0x0];
NSData *encrypted = [pair encrypt:data];
The pair variable holds references to a private key and a public key that were succesfully generated before using SecKeyGeneratePair.
The encrypt function looks like this:
- (NSData *)encrypt:(NSData *)data {
void *buffer = malloc([self blockSize] * sizeof(uint8_t));
memset(buffer, 0x0, [self blockSize]);
size_t ciphertextBufferLength = [data length];
OSStatus res = SecKeyEncrypt([self keyRef], 0x1, [data bytes], [data length], &buffer[0], &ciphertextBufferLength);
NSLog(#"Result of encryption: %d", res);
return [NSData dataWithBytesNoCopy:buffer length:[self blockSize] freeWhenDone:YES];
}
The implementation of [self blockSize] is rather straightforward:
- (unsigned long)blockSize {
return SecKeyGetBlockSize(keyRef);
}
I generate my keys with the following function:
- (BOOL)generateNewKeyPairOfSize:(unsigned int)keySize
{
SecKeyRef privKey = [[self publicKey] keyRef];
SecKeyRef pubKey = [[self publicKey] keyRef];
NSDictionary *privateKeyDict = #{ (__bridge id)kSecAttrIsPermanent : #(YES), (__bridge id)kSecAttrApplicationTag : [[self privateKey] tag] };
NSDictionary *publicKeyDict = #{ (__bridge id)kSecAttrIsPermanent : #(YES), (__bridge id)kSecAttrApplicationTag : [[self publicKey] tag] };
NSDictionary *keyDict = #{ (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA, (__bridge id)kSecAttrKeySizeInBits : #(keySize), (__bridge id)kSecPublicKeyAttrs : publicKeyDict, (__bridge id)kSecPrivateKeyAttrs : privateKeyDict };
OSStatus res = SecKeyGeneratePair((__bridge CFDictionaryRef)keyDict, &privKey, &pubKey);
NSLog(#"Result of generating keys: %d", res);
[[self publicKey] setKeyRef:pubKey];
[[self privateKey] setKeyRef:privKey];
return YES;
}
The problem is that the value of res is -4, meaning errSecUnimplemented according to the documentation. I'm not sure what I'm doing wrong here since I have all parameters required. I'm not sure whether there is a mistake in the parameters and where. The call the [self blockSize] return 128.
Can anyone help me with this?
From documentation:
cipherTextLen - On entry, the size of the buffer provided in the
cipherText parameter. On return, the amount of data actually placed in
the buffer.
You don't set any value to ciphertextBufferLength.
Update #1
In SecKeyGeneratePair() you have wrong arguments: public key argument must be first, private key is the second. I think that is the reason why you have error code -4.
Update #2
When you fix problem from Update #1 you will see error code -50 (errSecParam) because your cipher text length is wrong. Here is how correct encryption/decryption looks like:
[self generateNewKeyPairOfSize:1024];
NSData *data = [[NSData alloc] initWithBase64EncodedString:#"aGFsbG8=" options:0x0];
size_t cipherTextSize = [self blockSize];
uint8_t *cipherText = malloc(cipherTextSize);
memset(cipherText, 0, cipherTextSize);
OSStatus res = SecKeyEncrypt(_publicKey, kSecPaddingPKCS1, [data bytes], data.length, cipherText, &cipherTextSize);
NSLog(#"Result of encryption: %d", res);
size_t plainTextSize = cipherTextSize;
uint8_t *plainText = malloc(plainTextSize);
res = SecKeyDecrypt(_privateKey, kSecPaddingPKCS1, cipherText, cipherTextSize, plainText, &plainTextSize);
NSLog(#"Result of decryption: %d", res);
In addition to the correct answer above, what solved it for me was the following knowledge:
You will get -4 if you are trying to encrypt anything using a SecKeyRef that refers to a private key.
Think about it. Nothing that was encrypted with a private key would be secure because the public key is, well, public. /facepalm
So yeah, Apple's framework does the responsible thing, and simply blocks you from encrypting something with a private key. Because if it allowed you to do something that stupid, then it would be giving you a false sense of security, which would be irresponsible.
Related
How to fix Veracode Use After Free (CWE ID 416)
Recommendations from Veracode: Ensure that all pointers are set to NULL once the memory they point to has been freed.
Error pointed on: Line 8 "return result;"
+ (NSData *)dataFromBase64String:(NSString *)aString
{
NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
size_t outputLength = 0;
void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
free(outputBuffer);
return result;
}
Veracode scan does just help you to find places in code where you could improve security related coding. It does not stop attacks of course, but if your application is really that much security oriented you can make it more difficult to read out memory that is left after processing.
The word "Error" in the logging of Veracode is maybe a bit overused..
But my suggestion to address Veracodes Error pointed on: Line 8 "return result;" would be..
+ (NSData *)dataFromBase64String:(NSString *)aString
{
if (aString!=nil && [aString length]) {
size_t outputLength = 0;
void *outputBuffer = NULL;
NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
if (outputBuffer==NULL) return nil; //if NewBase64Decode() failed there is nothing to free..
NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
free(outputBuffer);
outputBuffer = NULL;
return result;
}
return nil;
}
This is because free'd memory is not set to NULL without your intent, so someone scanning memory for left overs would maybe find some clues about the former content of address.
here some nice discussion if NULL after free is really needed.
If you go that much into detail to avoid any kind of risk then you could also initiate outputBuffer with NULL (void* outputBuffer = NULL;) before you even use it.
Well it is another discussion if this is a bit overdo for some objC code where just swizzling could override the whole method.
EDIT: even more spagetti code, trying to avoid returning any value other than void and change a passed argument instead.
+ (void)dataFromBase64String:(NSString *)aString toResult:(NSData**)result
{
if (aString!=nil && [aString length]) {
size_t outputLength = 0;
void *outputBuffer = NULL;
NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
if (outputBuffer==NULL) return; //if NewBase64Decode() failed there is nothing to do
*result = [NSData dataWithBytes:outputBuffer length:outputLength];
free(outputBuffer);
outputBuffer = NULL;
}
}
//and call like..
NSData *myresult = nil;
[YOURCLASS dataFromBase64String:#"someString" toResult:&myresult];
NSLog(#"result=%#",myresult);
Now i wonder what Veracode is reporting with the edit above..
I'm using AES Encryption and Decryption in my project which is an apple watch project. So for encryption and decryption I created one category for NSData, And I'm calling those methods in my sample view controller class. Its working fine.
And then Now I copied the same code in to my ServerManager class where I should do actual encryption. But it is showing me this error.
Crash with Error :
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteData AES256EncryptWithKey:]: unrecognized selector sent to instance 0x7ab084a0'
The same code is working in sample code and it is showing me this error in my actual project. I didn't understand what could be the reason. I never heard about NSConcreteData. also. Here is my code follows.
In My ServerManager Class :
NSError *err;
NSDictionary *dict = #{#"accountType":#"ALL",#"uId":#"c8ff46be-a083-40
09-8a33-fc2d22cc40e3|123456784",#"deviceId":#"qvxy1234"};
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&err];
NSString *encryptedData = [[jsonData AES256EncryptWithKey:kABAESCryptionKey] base64EncodedStringWithOptions:0];
NSLog(#"%#",encryptedData);
NSData *nsdataFromBase64String = [[NSData alloc]
initWithBase64EncodedString:encryptedData options:0];
and NSData+CustomCategory Class is like this :
- (NSData *)AES256EncryptWithKey:(NSString *)key {
return [self AES256Operation:kCCEncrypt key:key];
}
- (NSData *)AES256DecryptWithKey:(NSString *)key {
return [self AES256Operation:kCCDecrypt key:key];
}
/*!
* #brief Common method for encrypt and decrypt
* #param Operation : pass required crypto CCOperation type
* #param key : pass key based on which crption is done
* #returns NSData object : Encrypted or Decrypted data
*/
- (NSData *)AES256Operation:(CCOperation)operation key:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1];
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesCrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesCrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
}
free(buffer); //free the buffer
return nil;
}
The same code is working in sample but not in my real project...
Addition :
It is not because of base64EncodedStringWithOptions: method.
I tried like this also
NSData *encryptedData = [jsonData AES256EncryptWithKey:kABAESCryptionKey];
So this code is also crashing, So I'm sure the problem is not with base64EncodedStringWithOptions:? some thing is going wrong while I'm using in IWatch extension classes.
Here the kABAESCryptionKey is a proper key which is working fine. in Sample example.
Yeah I got the answer. Its so dumb thing. This category is not added to my watch build target. So it is showing error. Now I added NSData+CustomCategory.m means i checked this file for my watch build target its working fine.
i think the problem is in AES256EncryptWithKey ..see link this may sure help
I have requirement in the app to do 2 level RSA encryption with 2 different public key.
1) Encrypt plainText with first public key.
2) Encrypt output of first with second public key.
I am using iOS Security framework to do the same. The first level of encryption works fine but when I try to encrypt again the output of step one with second public key the sanity check fails returning -50.
I assume this is due to the reason that buffer size is less for the required text to encrypt. But I am not sure what parameter to change to achieve the same. I tried changing padding from kSecPaddingPKCS1 to other types but it is not giving the required output.
Following is my function for the encryption:
+ (NSData*)getRSAEncryptedText:(NSString*)plaintext withPublicKeyIdSuffix:(NSString*)idSuffix {
SecKeyRef publicKey = NULL;
NSString *publicKeyIdentifier = [NSString stringWithFormat:#"%#.%#",[[NSBundle mainBundle] bundleIdentifier], idSuffix];
NSData * publicTag = [publicKeyIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
[queryPublicKey setObject:kSecClassKey forKey:kSecClass];
[queryPublicKey setObject:publicTag forKey:kSecAttrApplicationTag];
[queryPublicKey setObject:kSecAttrKeyTypeRSA forKey:kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:kSecReturnRef];
SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey);
if (!publicKey)
{
if(publicKey) CFRelease(publicKey);
return nil;
}
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
SecPadding kTypeOfWrapPadding = kSecPaddingPKCS1;
// SecPadding kTypeOfWrapPadding = kSecPaddingNone;
uint8_t* cipherBuffer = malloc( cipherBufferSize * sizeof(uint8_t) );
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
OSStatus sanityCheck = noErr;
// Encrypt using the public key.
sanityCheck = SecKeyEncrypt(publicKey,
kTypeOfWrapPadding,
(const uint8_t *)[[plaintext dataUsingEncoding:NSUTF8StringEncoding] bytes],
[[plaintext dataUsingEncoding:NSUTF8StringEncoding] length],
cipherBuffer,
&cipherBufferSize
);
if (sanityCheck != noErr) {
//NSLog(#"error with encryption");
free(cipherBuffer);
return nil;
}
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return encryptedData;
}
Please suggest how to achieve the second level of encryption.
The issue is resolved now, have altered the order in which keys were used for encryption. The one with smaller size is used first now for encryption and then the second one.
In my application, I have a public key (represented as string), plain message and digital signature, represented as base64 encoded string, hashed with SHA256 and encrypted with RSA).
Now, I need to verify digital signature. I was trying to do as follows:
create SecKeyRef from NSString (taken from here)
create SHA256 digest from original message
verify signature using SecKeyRawVerify function
(I am trying to avoid using OpenSSL function)
Additionally, my digital signature was created using Java's SHA256withRSA method. I was reading here that SHA256WithRSA appends algorithm identifier with the actual hash. Now, I am not sure whether or not I need to append it to the hash.
Anyway, in both cases I get error -50, which according to Apple's documentations means One
or more parameters passed to a function were not valid.
Here is my code:
-(BOOL) verifySignature:(NSString*) rawData andKey:(NSString*) key andSignature:(NSString*)signature {
NSData* originalData = [rawData dataUsingEncoding:NSUTF8StringEncoding];
NSData *signatureData = [NSData dataFromBase64String:signature];
SecKeyRef publicKey = [self generatePublicKey:key];
uint8_t sha2HashDigest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([originalData bytes], [originalData length], sha2HashDigest);
//DO I NEED THIS?
NSString *algIdentifier = #"1.3.14.3.2.26";
NSData *algData = [algIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSData* d_hash = [NSData dataWithBytes:sha2HashDigest length:CC_SHA256_DIGEST_LENGTH];
NSMutableData *concatenatedData = [NSMutableData data];
[concatenatedData appendData:algData];
[concatenatedData appendData:d_hash];
OSStatus verficationResult = SecKeyRawVerify (publicKey,
kSecPaddingPKCS1SHA256,
(const uint8_t *)[d_hash bytes],
(size_t)[d_hash length],
(const uint8_t *)[signatureData bytes],
(size_t)[signatureData length]
);
CFRelease(publicKey);
if (verficationResult == errSecSuccess){
NSLog(#"Verified");
return YES;
}
return NO;
}
- (SecKeyRef)generatePublicKey:(NSString *)key
{
// This will be base64 encoded, decode it.
NSData *d_key = [NSData dataFromBase64String:key];
d_key = [self stripPublicKeyHeader:d_key];
if (d_key == nil) return(nil);
NSData *d_tag = [NSData dataWithBytes:[#"pubKey" UTF8String] length:[#"pubKey" length]];
NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
[publicKey setObject:(id) kSecClassKey forKey:(id)kSecClass];
[publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[publicKey setObject:d_tag forKey:(id)kSecAttrApplicationTag];
SecItemDelete((CFDictionaryRef)publicKey);
CFTypeRef persistKey = nil;
// Add persistent version of the key to system keychain
[publicKey setObject:d_key forKey:(id)kSecValueData];
[publicKey setObject:(id) kSecAttrKeyClassPublic forKey:(id)
kSecAttrKeyClass];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)
kSecReturnPersistentRef];
OSStatus secStatus = SecItemAdd((CFDictionaryRef)publicKey, &persistKey);
if (persistKey != nil) CFRelease(persistKey);
if ((secStatus != noErr) && (secStatus != errSecDuplicateItem)) {
[publicKey release];
return(nil);
}
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
[publicKey removeObjectForKey:(id)kSecValueData];
[publicKey removeObjectForKey:(id)kSecReturnPersistentRef];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef
];
[publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
secStatus = SecItemCopyMatching((CFDictionaryRef)publicKey,
(CFTypeRef *)&keyRef);
[publicKey release];
return keyRef;
}
Maybe this answer is a little late but I had the same problem.
It turns out that Java handles the hashing for you, but iOS does not.
So if you have a plaintext called plainText you might generate a signature on it in Java doing this:
public static byte[] sign(PrivateKey key, byte[] plainText) {
try {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(key);
signature.update(plainText);
return signature.sign();
} catch (Exception e) {
return null;
}
}
But then to verify it in iOS you need to manually take a hash of the plaintext like so:
+ (BOOL)verifySignature:(uint8_t*)signature signatureLen:(size_t)sLen
withPlainText:(uint8_t*)plainText plainTextLen:(size_t)pLen
andKey:(SecKeyRef)key {
uint8_t hash[32];
CC_SHA256(plainText, pLen, hash);
OSStatus returnCode = SecKeyRawVerify(key,
kSecPaddingPKCS1SHA256,
hash,
32,
signature,
sLen);
return returnCode == 0;
}
In the above method, signature is the bytes generated by the Java method.
Of course, you may not want to hardcode parameters such as the the hash function used (and length of hash).
I have two scenarios of RSA encryption:
Key pair generated by c# app. Public key sent to iOS. iOS app encrypts data with the key and sends it back. The c# app decrypts using the private key. --- Completely implemented and working.
Key pair generated by ios app. Public key sent to c# app. The c# app encrypts the data, base 64 encodes it and sends it to the ios app. The ios app then decrypts the data using the private key. --- Not working.
If I encrypt using the public and private key pair generated on ios it all works, but as soon as I replace it with the base 64 encoded string from c# it fails. I have tried all combinations of padding.
The code is as follows:
SecPadding padding = kSecPaddingPKCS1;
NSMutableArray *keys = [self generateRSAKeyWithKeySizeInBits:1024 publicKeyTag:#"RSA Public Key" privateKeyTag:#"RSA Private Key"];
SecKeyRef test_publicKey = (__bridge SecKeyRef)(keys[0]);
SecKeyRef test_privateKey = (__bridge SecKeyRef)(keys[1]);
const char *sampled_utf8 = "VnWBW/xRyJB48Uxjdl99apczCuS07zhnLvIjnqyZIQqbI4F7kyAezfD1MNlgeTefkHuCRuzogaQTamk2XRwXoBoGy3Agj4ocPK2Wa7vWNGip8X3FAo1eJL+xKoVoqre/ipDjnZNfEUbX91Ru+IqWkbZXD2POlFfuMaTatCl50+U=";
NSString *sampled = [[NSString alloc] initWithUTF8String:sampled_utf8];
Byte sampled_inputData [[sampled lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[sampled dataUsingEncoding:NSUTF8StringEncoding] getBytes:sampled_inputData];//get the pointer of the data
size_t sampled_inputDataSize = (size_t)[sampled length];
size_t sampled_outputDataSize = EstimateBas64DecodedDataSize(sampled_inputDataSize);//calculate the decoded data size
Byte sampled_outputData[sampled_outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(sampled_inputData, sampled_inputDataSize, sampled_outputData, &sampled_outputDataSize);//decode the data
NSData *sampled_Data = [[NSData alloc] initWithBytes:sampled_outputData length:sampled_outputDataSize];//create a NSData
NSData *test_encrypted_Data = [self encryptString:encoded_toEncrypt RSAPublicKey:test_publicKey padding:padding];
size_t test_encrypted_inputDataSize = (size_t)[test_encrypted_Data length];
Byte *test_encrypted_inputData = (Byte*) malloc(test_encrypted_inputDataSize);
memcpy(test_encrypted_inputData,[test_encrypted_Data bytes],test_encrypted_inputDataSize);
size_t test_encrypted_outputDataSize = EstimateBas64EncodedDataSize(test_encrypted_inputDataSize);//calculate the encoded data size
char* test_encrypted_outputData[test_encrypted_outputDataSize];//prepare a char for the encoded data
Base64EncodeData(test_encrypted_inputData, test_encrypted_inputDataSize, test_encrypted_outputData, &test_encrypted_outputDataSize,false);//encode the data
NSData *test_encrypted_Encoded = [[NSData alloc] initWithBytes:test_encrypted_outputData length:test_encrypted_outputDataSize];//create a NSData object from the decoded data
size_t input_Size = (size_t)[test_encrypted_Encoded length];
Byte *input = (Byte*) malloc(input_Size);
memcpy(input,[test_encrypted_Encoded bytes],input_Size);
size_t output_Size = EstimateBas64DecodedDataSize(input_Size);
char* output[output_Size];
Base64DecodeData(input, input_Size, output, &output_Size);
NSData *res = [[NSData alloc] initWithBytes:output length:output_Size];
NSData *test_decryptedData = [self decryptString:sampled_Data RSAPrivateKey:test_privateKey padding:padding];
-(NSData*)decryptString:(NSData*)original RSAPrivateKey:(SecKeyRef)privateKey padding: (SecPadding)padding
{
#try
{
const unsigned char* original_String = (unsigned char *)[original bytes];
size_t decryptedLength = SecKeyGetBlockSize(privateKey);
uint8_t decrypted[decryptedLength];
OSStatus status = SecKeyDecrypt(privateKey,
padding,
original_String,
[original length],
decrypted,
&decryptedLength);
NSLog(#"result = %#", [self fetchStatus:status]);
if(status == noErr)
{
NSData* decryptedData = [[NSData alloc] initWithBytes:(const void*)decrypted length:decryptedLength];
return decryptedData;
}
else
return nil;
}
#catch (NSException * e)
{
//do nothing
NSLog(#"exception: %#", [e reason]);
}
return nil;
}
- (NSData*)encryptString:(NSString*)original RSAPublicKey:(SecKeyRef)publicKey1 padding:(SecPadding)padding
{
#try
{
Byte encrypt_inputData [[original lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[original dataUsingEncoding:NSUTF8StringEncoding] getBytes:encrypt_inputData];//get the pointer of the data
size_t encrypt_inputDataSize = (size_t)[original length];
size_t encrypt_outputDataSize = EstimateBas64DecodedDataSize(encrypt_inputDataSize);//calculate the decoded data size
Byte encrypt_outputData[encrypt_outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(encrypt_inputData, encrypt_inputDataSize, encrypt_outputData, &encrypt_outputDataSize);//decode the data
NSData *encryption_Data = [[NSData alloc] initWithBytes:encrypt_outputData length:encrypt_outputDataSize];//create a NSData object from the decoded data
size_t encryptedLength = SecKeyGetBlockSize(publicKey1);
uint8_t* sample = (uint8_t*)[encryption_Data bytes];
size_t text_Size = [encryption_Data length];
uint8_t *encrypted_Data_Bytes;
encrypted_Data_Bytes = malloc(sizeof(uint8_t)*encryptedLength);
memset(encrypted_Data_Bytes,0,encryptedLength);
OSStatus status = SecKeyEncrypt(publicKey1,
padding,
sample,
text_Size,
&encrypted_Data_Bytes[0],
&encryptedLength);
if(status == noErr)
{
NSData* encryptedData = [[NSData alloc] initWithBytes:(const void*)encrypted_Data_Bytes length:encryptedLength];
return encryptedData;
}
else
return nil;
}
#catch (NSException * e)
{
//do nothing
NSLog(#"exception: %#", [e reason]);
}
return nil;
}