Memory leak with SecPKCS12Import - ios

My app connects to my server using mutual authentification, so I have a .p12 file containing a certificate. Everything works the way it is supposed to, but when I'm profiling my app using Instruments, it detects a memory leak on this line :
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]){
NSData* p12data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"client" ofType:#"p12"]];
CFArrayRef itemsCFArray = nil;
NSDictionary* dico = [NSDictionary dictionaryWithObjectsAndKeys:#"password",kSecImportExportPassphrase, nil];
// MEMORY LEAK just below
OSStatus check = SecPKCS12Import((__bridge CFDataRef)p12data, (__bridge CFDictionaryRef)dico, &itemsCFArray);
if(check != noErr){
NSLog(#"Error importing PKCS");
}
NSArray* items = (__bridge NSArray*)itemsCFArray;
SecIdentityRef identityRef = (__bridge SecIdentityRef)[[items objectAtIndex:0] objectForKey:(__bridge id)kSecImportItemIdentity];
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:identityRef certificates:nil persistence:NSURLCredentialPersistenceNone];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
I've tried to use a CFDictionaryRef instead, but it does not resolve the error.
I found someone with the same problem, but his solution is ios4, and i'm using ios5 (Actually, I'm already doing the same thing) : http://www.ipup.fr/forum/viewtopic.php?id=2855 (in french, sorry)
How can I resolve this ? Will Apple reject my app because of this memory leak ?

I don't think the issue is with the dictionary, itemsCFArray is what appears to be getting leaked. SecPKCS12Import passes back a CF reference to itemsCFArray, which you will need to CFRelease when you are done using the objects in it.
Try calling CFRelease(itemsCFArray) after creating the credential.

Related

Are items stored using KeychainTouchID (relatively more) secure on Jailbroken devices?

My understanding of the iOS keychain is that is insecure on jailbroken devices.
I was wondering if one stores a value in the keychain using kSecAccessControlTouchIDAny as it's used in this example from Apple, if there is an extra level of protection, even on jailbroken devices?
Relevant excerpt of example where text is stored into the keychain:
- (void)addTouchIDItemAsync {
CFErrorRef error = NULL;
// Should be the secret invalidated when passcode is removed? If not then use kSecAttrAccessibleWhenUnlocked
SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
kSecAccessControlTouchIDAny, &error);
if (sacObject == NULL || error != NULL) {
NSString *errorString = [NSString stringWithFormat:#"SecItemAdd can't create sacObject: %#", error];
self.textView.text = [self.textView.text stringByAppendingString:errorString];
return;
}
/*
We want the operation to fail if there is an item which needs authentication so we will use
`kSecUseNoAuthenticationUI`.
*/
NSData *secretPasswordTextData = [#"SECRET_PASSWORD_TEXT" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *attributes = #{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: #"SampleService",
(__bridge id)kSecValueData: secretPasswordTextData,
(__bridge id)kSecUseNoAuthenticationUI: #YES,
(__bridge id)kSecAttrAccessControl: (__bridge_transfer id)sacObject
};
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, nil);
NSString *message = [NSString stringWithFormat:#"SecItemAdd status: %#", [self keychainErrorToString:status]];
[self printMessage:message inTextView:self.textView];
});
}
It depends how you define secure. Remember, on jailbroken devices any API can be subject to a man in the middle attack. So the backend storage mechanism may be irrelevant. An attacker on a jailbroken device can simply intercept your call to the Keychain API and read any of the arguments you pass to it.

iOS: Having problems with self-signed certificate. SSL Handshake Error (-9824)

I have an application where I need to perform sort of a 2-Step-Authentication and long story short I'm getting per-user base64 encoded pem format certificates from the server and using them on each request.
First I generate a key pair, make a CSR, give them the CSR, they give me the certificate and this is where I have to use it and fail. I'm getting the following errors in the console for each individual request:
CFNetwork SSLHandshake failed (-4)
CFNetwork SSLHandshake failed (-9824)
CFNetwork SSLHandshake failed (-9824)
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
My approach is the following:
-grab the DER encoded data from the PEM formatted signed certificate they are sending me
-make a SecCertificateRef which I add to the keychain
-query for the SecIdentityRef in the keychain by label
-I then do some mostly needless stuff like grab the SecCertificateRef and private key from the identity mostly to be sure what's going on
-I also insert a CA Certificate which I have from the server and grab a reference to it from the keychain (not sure if I need to use it for the credential but I tried with or without it - the result was the same)
-I then initialize the credential with the identity and my certificates and use it when I get a NSURLAuthenticationMethodClientCertificate auth method (I don't do the check but that's all I get besides the server trust).
So up to this point nothing is NULL, everything gets initialized and looks good but the requests don't succeed. When I try to use the server trust credential on all requests, I get through and don't get the error but my server is giving me a security error as it should. As soon as I use the custom credential for any challenge I get the above errors.
note: I know the code is messy and I shouldn't be inserting certificates on each request but it's still very early work in progress and that's not the problem since the refs get instantiated correctly
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
SSLConnectionWrapper *wrapper = [self wrapperForConnection:connection];
NSString *certStringBase64 = [[NSUserDefaults standardUserDefaults] SSLCertificateForUserWithID:wrapper.userID];
NSData *certData = [[NSData alloc] initWithBase64EncodedString:certStringBase64 options:0];
NSString *certString = [[NSString alloc] initWithData:certData encoding:NSUTF8StringEncoding];
certString = [certString stringByReplacingOccurrencesOfString:#"-----BEGIN CERTIFICATE-----" withString:#""];
certString = [certString stringByReplacingOccurrencesOfString:#"-----END CERTIFICATE-----" withString:#""];
certString = [[certString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
//at this point certString contains the DER encoded certificate data
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)([[NSData alloc] initWithBase64EncodedString:certString options:kNilOptions]));
OSStatus err = SecItemAdd((__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id) kSecClassCertificate, kSecClass,
cert, kSecValueRef,
kCFBooleanTrue, kSecReturnPersistentRef,
[NSString stringWithFormat:#"CertLabel_UserID_%#", wrapper.userID], kSecAttrLabel,
nil], NULL);
const void *keys[] = { kSecClass, kSecReturnRef, kSecAttrLabel };
const void *values[] = { kSecClassIdentity, kCFBooleanTrue, (__bridge const void *)([NSString stringWithFormat:#"CertLabel_UserID_%#", wrapper.userID]) };
CFDictionaryRef queryForIdentityDict = CFDictionaryCreate(NULL, keys, values,
3, NULL, NULL);
SecIdentityRef identityKeychainRef = NULL;
OSStatus s = SecItemCopyMatching(queryForIdentityDict, (CFTypeRef *)&identityKeychainRef);
SecCertificateRef certKeychainRef = NULL;
OSStatus s2 = SecIdentityCopyCertificate(identityKeychainRef, &certKeychainRef);
SecKeyRef privateKey;
SecIdentityCopyPrivateKey(identityKeychainRef, &privateKey);
NSString *stringForCACert = [self stringForCACert];
SecCertificateRef caCert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)([[NSData alloc] initWithBase64EncodedString:stringForCACert options:kNilOptions]));
OSStatus s3 = SecItemAdd((__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id) kSecClassCertificate, kSecClass,
caCert, kSecValueRef,
#"CACert", kSecAttrLabel,
nil], NULL);
const void *keys1[] = { kSecClass, kSecReturnRef, kSecAttrLabel };
const void *values1[] = { kSecClassCertificate, kCFBooleanTrue, #"CACert" };
CFDictionaryRef queryForCACert = CFDictionaryCreate(NULL, keys1, values1,
3, NULL, NULL);
SecCertificateRef caCertKeychainRef = NULL;
OSStatus s4 = SecItemCopyMatching(queryForCACert, (CFTypeRef *)&caCertKeychainRef);
NSURLCredential *credential = [[NSURLCredential alloc] initWithIdentity:identityKeychainRef certificates:#[ (__bridge id)certKeychainRef, (__bridge id) caCertKeychainRef] persistence:NSURLCredentialPersistencePermanent];
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}else{
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
}
It's an authentication challenge faced by server. You can bypass by following code (Using NSURLCOnnection)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host {
return YES;
}
Note: Don't use above if you uploading app on app store.
For iOS 9 above would not work, please edit plist as follows
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

SecKeyRawVerify verifies on mac but fails with -9809 on iOS

I need to digitally sign on mac some data and then verify it on iOS. So I generated RSA keypair and certificate for public key in DER format with open ssl (tried generation with SecKeyGeneratePair but then it is harder to import Public key to iOS and SecKeyRawVerify still doesn't work with the same result), and signed my data on Mac app. Then if I verify this data on iOS verification fails with -9809 error code, but if execute the same code on mac verification succeeds.
Here is my code for verification:
NSString* certPath = [[NSBundle mainBundle] pathForResource: #"Public" ofType:#"der"];
NSData* certificateData = [NSData dataWithContentsOfFile: certPath];
SecCertificateRef certificateFromFile = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData); // load the certificate
SecPolicyRef secPolicy = SecPolicyCreateBasicX509();
SecTrustRef trust;
OSStatus statusTrust = SecTrustCreateWithCertificates( certificateFromFile, secPolicy, &trust);
SecTrustResultType resultType;
OSStatus statusTrustEval = SecTrustEvaluate(trust, &resultType);
SecKeyRef publicKey = SecTrustCopyPublicKey(trust);
NSString* licensingPolicyString = #"ZKL3YXtqtFcIeWRqSekNuCmtu/nvy3ApsbJ+8xad6cO/E8smLHGfDrTQ3h/38d0IMJcUThsVMyX8qtqILmPeTnBpZgJetBjb8kAfuPznzxOrIcYd27/50ThWv6guLqZL7j1apnfRZHAdMiozvEYH62sma1Q9qTl+W7qxEAxWs2AXDTQcF7nGciEM6MEohs8u879VNIE1VcPW8ahMoe25wf8pvBvrzE0z0MR4UFE3ZSWIeeQsiaUPYFwHbfQAOifaw/qIisjL5Su6WURoaSupWTMdQh3ZNyqZuYJaT70u8S7NgF3BzG8uBiYOUYsf6UayvkABmF0UuMdcvhPQefyhuXsiYWxsb3dFeGNoYW5nZSI6dHJ1ZSwiYWxsb3dTaGFmZXIiOnRydWUsInBvbGljeSBuYW1lIjp0cnVlfQ==";
size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
NSData* messageData = [[NSData alloc] initWithBase64EncodedData:[licensingPolicyString dataUsingEncoding: NSUTF8StringEncoding] options:0];
NSData* signatureData = [messageData subdataWithRange:NSMakeRange(0, signedHashBytesSize)];
NSData* rawMessageData = [messageData subdataWithRange: NSMakeRange(signedHashBytesSize, messageData.length - signedHashBytesSize)];
uint8_t sha1HashDigest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1([rawMessageData bytes], (CC_LONG)[rawMessageData length], sha1HashDigest);
OSStatus verficationResult = SecKeyRawVerify(publicKey, kSecPaddingPKCS1SHA1, sha1HashDigest, CC_SHA1_DIGEST_LENGTH, [signatureData bytes], [signatureData length]);
CFRelease(publicKey);
CFRelease(trust);
CFRelease(secPolicy);
CFRelease(certificateFromFile);
if (verficationResult == errSecSuccess) NSLog(#"Verified");
Is there some difference in digital signature verification for Mac and iOS? I didn't manage to find anything about it in Apple's documentation.
Well after some experimenting with sign/verify, I've found out that changing padding agreement to SecKeyRawVerify/SecKeyRawSign from kSecPaddingPKCS1SHA1 to kSecPaddingPKCS1, solves my problem. Don't know why it doesn't work with kSecPaddingPKCS1SHA1, there is not deprecations described in Apple's documentation. Also I didn't try this code on iOS different from 8.3 so maybe it is iOs8.3 issue.

SSL Certificate confusion

I am clearly missing the Aha! moment because I have been doing research on using CA or Self signed Certificates and such for gaining access to a secure URL https and I am still having trouble fully understanding it, I am mostly just blundering about with other people code and solutions to try and get mine working, i am clearly lacking a fundamental understanding so hopefully the denizens of the site can help.
Essentially, i have an app that communicates, with a https server using a self signed certificate.
What I believe to be the certificate and key needed to access the server are both stored in a p12 stored in the root bundle of the app. I then add this p12 to the phones or apps keychain through this code I found on the internet
NSString *p12Path = [[NSBundle mainBundle] pathForResource:p12Name ofType:#"p12"];
NSData *p12Data = [[NSData alloc] initWithContentsOfFile:p12Path];
NSError *error = nil;
NSData *data = [[SFHFKeychainUtils getPasswordForUsername:[[UIDevice currentDevice]name] andServiceName:serviceName error:&error] dataUsingEncoding:NSUTF8StringEncoding];
[self transform:data];
NSString *pass = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
CFStringRef password = (__bridge CFStringRef)pass;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef p12Items;
OSStatus result = SecPKCS12Import((__bridge CFDataRef)p12Data, optionsDictionary,&p12Items);
if(result == noErr)
{
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);
SecCertificateRef certRef;
SecIdentityCopyCertificate(identityApp,&certRef);
SecCertificateRef certArray[1] = { certRef };
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
CFRelease(certRef);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:(__bridge NSArray *)myCerts persistence:NSURLCredentialPersistencePermanent];
CFRelease(myCerts);
[[RKObjectManager sharedManager].HTTPClient setDefaultCredential:credential];
}
And, well this seems to work, But i have to have this enabled
_httpClient = [RKObjectManager sharedManager].HTTPClient;
[_httpClient setAllowsInvalidSSLCertificate:YES];
Otherwise it does not connect, Now I have seen various posts saying, you need to have this set to yes to allow self signed certs, but at the same time i have seen other posts saying that it should only be for development as otherwise this makes using https entirely redundant. And obviously redundant security is bad so i set it to no...and it does not connect.
So Really does anyone have any links or could spare some time themselves filling up the gaps in my knowledge on how this stuff works? it would be much appreciated and would save me getting grief from the boss.
This needs to be handled by AFNetworking and is called 'SSL Pinning'. Check the docs here for details on how to enable the feature and supply your certificate. There is also useful information here.

dataWithContentsOfURL - Received Memory warning handling

I'm working with ARC, and using this code for loading data from a 17.4 pdf crypted file.
NSData *fileData =[NSData dataWithContentsOfURL:docUrl];
NSMutableData *decPdf = // decrypt using third party library
CFDataRef PDFData = (__bridge CFDataRef)decPdf;
CGDataProviderRef provider = CGDataProviderCreateWithCFData(PDFData);
PDFDocument = CGPDFDocumentCreateWithProvider(provider);
CGDataProviderRelease(provider);
on iPad1 after a Low Memory warning app crashes on NSData *fileData =[NSData dataWithContentsOfURL:docUrl]. Is there something that I can do for managing memory in a better way in this part? If not, how should I handle the memory warning properly for preventing crashes?
As a quick check you can try wrapping the code in a separate #autoreleasepool{} block.
#autoreleasepool {
NSData *fileData =[NSData dataWithContentsOfURL:docUrl];
NSMutableData *decPdf = // decrypt using third party library
CFDataRef PDFData = (__bridge CFDataRef)decPdf;
CGDataProviderRef provider = CGDataProviderCreateWithCFData(PDFData);
PDFDocument = CGPDFDocumentCreateWithProvider(provider);
CGDataProviderRelease(provider);
}

Resources