iOS 4.3 extractIdentityAndTrust linking error - iphone

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

Related

Authenticate certificate chaining with server in iOS

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);
}
}

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!^-^

Client certificates iOS

I'm trying to extract a .p12 file and use it for a two way authentication against my own server. when i'm trying to compile i get some linking errors. The errors are refering to:
_kSecImportExportPassphrase
_SecIdentityCopyCertificate
_kSecImportItemTrust
_SecPKCS12Import
_kSecImportItemIdentity
Here is the code i'm using to extract the p12 file:
-(void)clientCert
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"torbix" ofType:#"p12"];
NSData *p12data = [NSData dataWithContentsOfFile:path];
CFDataRef inP12data = (CFDataRef)p12data;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
OSStatus status = extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
const void *certs[] = { myCertificate };
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);
}
OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, 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(inP12data, 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;
}
Why do i get these errors?
Add the Security.framework to your project.
In Xcode 4.2+ in the target go to the "Build Phases" tab, "Link BinariesWith Libraries", Click the "+" and add the "Security.framework".