Authenticate certificate chaining with server in iOS - iphone

I have to authenticate certificate chain with my root certificate against server. I get the server certificate's NSData.
We don't have any authenticationChallenge mechanism and need to verify using the NSData.
How do I achieve this, because SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure.
Following is the code I am using: -
NSData *aServerCertificateData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ABC" ofType:#"cer"]]; // This will actually come from the server
NSString *aRootPath = [[NSBundle mainBundle] pathForResource:#"XYZ" ofType:#"pem"];
CFDataRef aRootCertData = (__bridge CFDataRef)[NSData dataWithContentsOfFile:aRootPath];
CFDataRef myCertData = (__bridge CFDataRef)aServerCertificateData;
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecCertificateRef certArray[1] = {myCert};
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(myCerts, myPolicy, &myTrust);
SecCertificateRef rootCertRef[1] = {rootCert};
CFArrayRef rootCerts = CFArrayCreate(NULL, (void *)rootCertRef, 1, NULL);
status = SecTrustSetAnchorCertificates(myTrust, rootCerts);
SecTrustResultType trustResult;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult);
if (status == noErr) {
if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
NSData *exceptions = (__bridge NSData *)(SecTrustCopyExceptions(myTrust)); // This always gets called
NSLog(#"Exceptions: %#", [NSPropertyListSerialization propertyListWithData:exceptions options:kNilOptions format:nil error:nil]);
}
publicKeyRef = SecTrustCopyPublicKey(myTrust);
}
}

Related

OAuth signature with RSA-SHA1 on iOS

I need help creating an RSA-SHA1 signature to be used in a 3-legged OAuth implementation on iOS.
I was able to do this using HMAC-SHA1 using CommonCrypto.h, but this library doesn't seem to support RSA-SHA1.
Have any of you implemented OAuth signatures with RSA? Could you point me to some resources where I can find more information?
Thanks.
The answer by Erik Villegas was also the solution for me. But there is a bug in the posted code which I encountered when using this solution: secretFile was opened with fopen(), so it must be closed with fclose()
- (NSString *)RSASHA1HashForString:(NSString *)source {
NSLog(#"encrypting %#", source);
if (source == nil) return nil;
OpenSSL_add_all_algorithms();
NSString *signature = nil;
// make a SHA-1 digest of the source string
const char* sourceChars = [source UTF8String];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)sourceChars, strlen(sourceChars), digest);
NSString *path = [[NSBundle mainBundle] pathForResource:#"privatekey" ofType:#"pem"];
const char *pathCString = [path cStringUsingEncoding:NSUTF8StringEncoding];
FILE *secretFile = fopen(pathCString, "r");
RSA *rsa = NULL;
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
if (rsa != NULL) {
unsigned int sigLen = 0;
unsigned char *sigBuff = malloc(RSA_size(rsa));
int result = RSA_sign(NID_sha1, digest, (unsigned int) sizeof(digest),
sigBuff, &sigLen, rsa);
if (result != 0) {
NSData *sigData = [NSData dataWithBytes:sigBuff length:sigLen];
signature = [self base64forData:sigData];
}
free(sigBuff);
RSA_free(rsa);
}
fclose(secretFile);
NSLog(#"generated signature: %#", signature);
return signature;
}
I finally found the solution. Here is a method that will look for a privatekey.pem file in your bundle and create a RSA-SHA1 signature using the string that is passed in. You will need to add the openssl library. You can use this project as a reference: https://github.com/x2on/OpenSSL-for-iPhone
- (NSString *)RSASHA1HashForString:(NSString *)source {
NSLog(#"encrypting %#", source);
if (source == nil) return nil;
OpenSSL_add_all_algorithms();
NSString *signature = nil;
// make a SHA-1 digest of the source string
const char* sourceChars = [source UTF8String];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)sourceChars, strlen(sourceChars), digest);
NSString *path = [[NSBundle mainBundle] pathForResource:#"privatekey" ofType:#"pem"];
const char *pathCString = [path cStringUsingEncoding:NSUTF8StringEncoding];
FILE *secretFile = fopen(pathCString, "r");
RSA *rsa = NULL;
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
if (rsa != NULL) {
unsigned int sigLen = 0;
unsigned char *sigBuff = malloc(RSA_size(rsa));
int result = RSA_sign(NID_sha1, digest, (unsigned int) sizeof(digest),
sigBuff, &sigLen, rsa);
if (result != 0) {
NSData *sigData = [NSData dataWithBytes:sigBuff length:sigLen];
signature = [self base64forData:sigData];
}
free(sigBuff);
RSA_free(rsa);
}
NSLog(#"generated signature: %#", signature);
return signature;
}
If you are implementing OAuth you'll need to pass the signature base into this method. More info can be found here: http://oauth.net/core/1.0a/#anchor13
Here is implementation using core security iOS framework.
Key should be converted to pkcs12 format.
Algorythm name is "kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1"
- (NSString *)getStringSHA1RSASignature:(NSString *)str
{
NSString *path = [[NSBundle bundleForClass:[self class]]
pathForResource:#"rsaPrivate.pfx" ofType:#"pkcs12"];
NSData *p12data = [NSData dataWithContentsOfFile:path];
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
[options setObject:#" your password " forKey:(id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((CFDataRef)p12data, (CFDictionaryRef)options, &items);
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp =
(SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
assert(securityError == noErr);
SecKeyRef privateKeyRef;
SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
/// have a key
NSData *input = [str dataUsingEncoding:NSUTF8StringEncoding];
CFErrorRef error = nil;
///////////////////////////////// "SHA1withRSA" java
CFDataRef signature = SecKeyCreateSignature(privateKeyRef, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1, (CFDataRef)input, &error);
NSAssert(signature != nil, #"");
NSString *result = [(__bridge NSData *)signature base64EncodedStringWithOptions:0];
CFRelease(signature);
NSAssert(result != nil, #"");
NSLog(#"generated signature: %#", result);
return result;
}

SecPKCS12Import leak under arc?

I was found memory leaks by iOS instruments on all SecPKCS12Import lines of that code under arc:
SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trustRef, 0);
CFStringRef certSummary = SecCertificateCopySubjectSummary(certRef);
NSData *data = (__bridge_transfer NSData *) SecCertificateCopyData(certRef);
NSURL *indexURL = [[NSBundle mainBundle] URLForResource:#"cert1" withExtension:#"p12"];
NSData *localP12 = [NSData dataWithContentsOfURL:indexURL];
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
NSString *password = ///
[options setObject:password forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) localP12,(__bridge CFDictionaryRef)options, &items);
if (securityError == noErr) { };/// good } else { //bad }
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
CFArrayRef certificates =
(CFArrayRef)CFDictionaryGetValue(identityDict,kSecImportItemCertChain);
SecCertificateRef localCert = (SecCertificateRef)CFArrayGetValueAtIndex(certificates,0);
CFDataRef dataLocal = SecCertificateCopyData(localCert);
NSData *local = (__bridge NSData *)dataLocal;
//NSLog(#"local:%#",local);
NSURL *indexURLmac3 = [[NSBundle mainBundle] URLForResource:#"cert2" withExtension:#"p12"];
NSData *localP12mac3 = [NSData dataWithContentsOfURL:indexURLmac3];
NSMutableDictionary * optionsMac3 = [[NSMutableDictionary alloc] init];
NSString *passwordMac3 = //
[optionsMac3 setObject:passwordMac3 forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef itemsMac3 = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((__bridge CFDataRef) localP12mac3, (__bridge CFDictionaryRef)optionsMac3, &itemsMac3);
if (securityError == noErr) { };/// good } else { //bad }
CFDictionaryRef identityDictMac3 = CFArrayGetValueAtIndex(itemsMac3, 0);
CFArrayRef certificatesMac3 =
(CFArrayRef)CFDictionaryGetValue(identityDictMac3, kSecImportItemCertChain);
SecCertificateRef localCertMac3 = (SecCertificateRef)CFArrayGetValueAtIndex(certificatesMac3,0);
CFDataRef dataLocalMac3 = SecCertificateCopyData(localCertMac3);
NSData *localMac3 = (__bridge NSData *)dataLocalMac3;
NSURL *indexURLwebcob3 = [[NSBundle mainBundle] URLForResource:#"cert3" withExtension:#"p12"];
NSData *localP12wwebcob3 = [NSData dataWithContentsOfURL:indexURLwebcob3];
NSMutableDictionary * optionsWebcob3 = [[NSMutableDictionary alloc] init];
NSString *passwordWebcob3 = //
[optionsWebcob3 setObject:passwordWebcob3 forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef itemsWebcob3 = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import((__bridge CFDataRef) localP12wwebcob3, (__bridge CFDictionaryRef)optionsWebcob3, &itemsWebcob3);
if (securityError == noErr) { };/// good } else { //bad }
CFDictionaryRef identityDictWebcob3 = CFArrayGetValueAtIndex(itemsWebcob3, 0);
CFArrayRef certificatesWebcob3 =
(CFArrayRef)CFDictionaryGetValue(identityDictWebcob3,
kSecImportItemCertChain);
SecCertificateRef localCertWebcob3 = (SecCertificateRef)CFArrayGetValueAtIndex(certificatesWebcob3,0);
CFDataRef dataLocalWebcob3 = SecCertificateCopyData(localCertWebcob3);
NSData *localWebcob3 = (__bridge NSData *)dataLocalWebcob3;
if ([data isEqualToData:local] || [data isEqualToData:localMac3] || [data isEqualToData:localWebcob3]) trust = YES;
CFRelease(certSummary);
CFRelease((CFDataRef) dataLocal);
CFRelease((CFDataRef) dataLocalMac3);
CFRelease((CFDataRef) dataLocalWebcob3);
where i'm wrong?
Wow. That code is really hard to follow. You seem to be doing three different PKCS12 imports, you may want to make that a single method that's called three times. Just saying.
Anyway, without even following your code I know what the issue may be - because I have seen this before. The Security methods you are using follow the CoreFoundation memory management patterns defined here. More than once I have found the PKCS12 identity import process leaking because someone did not realize that, or thought that bridge casts to ARC would just make it work.
But here is what you should look at - in addition to whatever Instruments is trying to tell you, of course:
You need to release the items passed as the last argument to SecPKCS12Import (the CFArrayRef in the documentation). Look at Apple's example for guidance.
I see something even more obvious - you are calling CFArrayCreate without a corresponding release.

get pfx file included in a mobilecertificate in iOS

I'm trying to connect to a server using a .pfx that is stored in a .mobileconfig file on my iPhone.
When the server ask for it in
-(void)connection:(NSURLConnection*)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge{
How can I create the NSURLCredential with the .pfx? Should I use
+ (NSURLCredential *)credentialWithIdentity:(SecIdentityRef)identity certificates:(NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence
If thats the case, how do I extract the .pfx to put it into the array.
Thanks in advance.
So no, there is no way to get the certificate from the mobileconfig file. iOS applications use its own keychain access and storage. Only email and other phone service like internet can make use of those certificates
U can use my code:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"torbix" ofType:#"pfx"];
NSData *pfxdata = [NSData dataWithContentsOfFile:path];
CFDataRef inpfxdata = (CFDataRef)pfxdata;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
OSStatus status = extractIdentityAndTrust(inpfxdata, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
const void *certs[] = { myCertificate };
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity
certificates:(NSArray *)myCertificate
persistence:NSURLCredentialPersistencePermanent];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
CFRelease(myIdentity);
CFRelease(myCertificate);
CFRelease(certsArray);
}
//extractIdentityAndTrust method.
-(OSStatus) extractIdentityAndTrust:(CFDataRef)inpfxdata identity:(SecIdentityRef *)identity trust:(SecTrustRef *)trust
{
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inpfxdata, options, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
*trust = (SecTrustRef)tempTrust;
}
if (options) {
CFRelease(options);
}
return securityError;
}
good luck!^-^

iOS 4.3 extractIdentityAndTrust linking error

I'm having a problem using extractIdentityAndTrust in iOS and getting the following linking error. I'm just trying to follow the code from the 'Certificate,Key and trust programming guide' and have a PKCS#12 certificate in the bundle.
"_extractIdentityAndTrust", reference from:
[cryptoViewController viewDidLoad] in cryptoViewController.o
Symbol(s) not found
Collect2: Id returneed 1 exit status
I've the following code in the project;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *thePath = [[NSBundle mainBundle]
pathForResource:#"iphone-cert" ofType:#"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (CFDataRef)PKCS12Data;
CFDataRef inPKCS12Data1 = (CFDataRef)PKCS12Data;
OSStatus status = noErr;
SecIdentityRef myIdentity;
SecIdentityRef *outIdentity;
SecTrustRef *outTrust;
SecTrustRef myTrust;
status = extractIdentityAndTrust(
inPKCS12Data1,
&myIdentity,
&myTrust);
if (status != 0)
{
}
SecTrustResultType trustResult;
if (status == noErr)
{
status = SecTrustEvaluate(myTrust, &trustResult);
}
if (trustResult == kSecTrustResultRecoverableTrustFailure)
{
}
OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
SecIdentityRef *outIdentity,
SecTrustRef *outTrust);
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("Password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(
NULL, keys,
values, 1,
NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
CFDataRef inPKCS12Data2 = (CFDataRef)PKCS12Data;
securityError = SecPKCS12Import(inPKCS12Data2,
optionsDictionary,
&items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,
kSecImportItemIdentity);
*outIdentity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
*outTrust = (SecTrustRef)tempTrust;
if (optionsDictionary)
CFRelease(optionsDictionary);
[PKCS12Data release];
}
//Next part
SecCertificateRef myReturnedCertificate = NULL;
SecIdentityRef myReturnedIdentity;
status = SecIdentityCopyCertificate (myReturnedIdentity,
&myReturnedCertificate);
CFStringRef certSummary = SecCertificateCopySubjectSummary
(myReturnedCertificate);
NSString* summaryString = [[NSString alloc]
initWithString:(NSString*)certSummary]; //
NSLog(#"%#", summaryString);
[summaryString release];
}
and the following declaration in the header file;
OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
SecIdentityRef *outIdentity, SecTrustRef *outTrust);
Has anyone got any advice?
I'm not sure, but it seams that this method is not available on iOS.
Anyway the proper way to get the identity and certificates from a p12 file is:
Use the SecPKCS12Import() function to import the p12 data.
This will return an NSArray containing NSDictionary objects.
The identity is stored within the dictionary under the key 'kSecImportItemIdentity'
An NSArray of certificates is stored under 'kSecImportItemCertChain'
The things get a little bit complicated if you have multiple identities in your p12 file. Then you need to have some logic on how to choose the right one. But for start just get the dictionary at index 0 from the array returned at step 1 ;-)
Regards,
Pece

Why does Keychain Services return the wrong keychain content?

I've been trying to use persistent keychain references in an iPhone application. I found that if I created two different keychain items, I would get a different persistent reference each time (they look like 'genp.......1', 'genp.......2', …). However, attempts to look up the items by persistent reference always returned the content of the first item. Why should this be? I confirmed that my keychain-saving code was definitely creating new items in each case (rather than updating existing items), and was not getting any errors. And as I say, Keychain Services is giving a different persistent reference for each item.
I've managed to solve my immediate problem by searching for keychain items by attribute rather than persistent references, but it would be easier to use persistent references so I'd appreciate solving this problem.
Here's my code:
- (NSString *)keychainItemWithName: (NSString *)name {
NSString *path = [GLApplicationSupportFolder()
stringByAppendingPathComponent: name];
NSData *persistentRef = [NSData dataWithContentsOfFile: path];
if (!persistentRef) {
NSLog(#"no persistent reference for name: %#", name);
return nil;
}
NSArray *refs = [NSArray arrayWithObject: persistentRef];
//get the data
CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(params, kSecMatchItemList, refs);
CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(params, kSecReturnData, kCFBooleanTrue);
CFDataRef item = NULL;
OSStatus result = SecItemCopyMatching(params, (CFTypeRef *)&item);
CFRelease(params);
if (result != errSecSuccess) {
NSLog(#"error %d retrieving keychain reference for name: %#", result, name);
return nil;
}
NSString *token = [[NSString alloc] initWithData: (NSData *)item
encoding: NSUTF8StringEncoding];
CFRelease(item);
return [token autorelease];
}
- (void)setKeychainItem: (NSString *)newToken forName: (NSString *)name {
NSData *tokenData = [newToken dataUsingEncoding: NSUTF8StringEncoding];
//firstly, find out whether the item already exists
NSDictionary *searchAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
name, kSecAttrAccount,
kCFBooleanTrue, kSecReturnAttributes,
nil];
NSDictionary *foundAttrs = nil;
OSStatus searchResult = SecItemCopyMatching((CFDictionaryRef)searchAttributes,
(CFTypeRef *)&foundAttrs);
if (noErr == searchResult) {
NSMutableDictionary *toStore = [foundAttrs mutableCopy];
[toStore setObject: tokenData forKey: (id)kSecValueData];
OSStatus result = SecItemUpdate((CFDictionaryRef)foundAttrs,
(CFDictionaryRef)toStore);
if (result != errSecSuccess) {
NSLog(#"error %d updating keychain", result);
}
[toStore release];
return;
}
//need to create the item.
CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(params, kSecAttrAccount, name);
CFDictionaryAddValue(params, kSecReturnPersistentRef, kCFBooleanTrue);
CFDictionaryAddValue(params, kSecValueData, tokenData);
NSData *persistentRef = nil;
OSStatus result = SecItemAdd(params, (CFTypeRef *)&persistentRef);
CFRelease(params);
if (result != errSecSuccess) {
NSLog(#"error %d from keychain services", result);
return;
}
NSString *path = [GLApplicationSupportFolder()
stringByAppendingPathComponent: name];
[persistentRef writeToFile: path atomically: NO];
[persistentRef release];
}
Turns out that using the kSecMatchItemList doesn't appear to work at all.
I did mine like this:
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword, kSecClass,
persistentRef, (id)kSecValuePersistentRef,
(id)kCFBooleanTrue, kSecReturnAttributes,
(id)kCFBooleanTrue, kSecReturnData,
nil];
NSDictionary *result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query,
(CFTypeRef*)&result);
which returned the attributes and data for the persistent reference. The documentation in the header about converting a "persistent reference" into a "standard reference" makes no sense at all. Hope this helps.