Question

In my app, i need to authenticate the user based on a certficate. This is how i do it 1. First of all, i export the certificate into the sandbox. 2. Then i extract the SecIdentityRef from the certificate, add it to the keychain and later delete the certificate from the sandbox. 3. When i actually pass this identity to the NSURL connection's authentication challenge, the app just crashes without giving any meaningful information.

Below are all relevant code snippets

//Exporting the certificate into the app and extracting the identity from it

- (void)importCertificateIntoKeychain:(NSString *)Password
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"Certificates"];
    NSString *certFolderName = [[[[NSUserDefaults standardUserDefaults] objectForKey:@"CertificatePath"] lastPathComponent]stringByDeletingPathExtension];
    NSString *path = [NSString stringWithFormat:@"%@/%@",dataPath,certFolderName];
    NSArray*array = [[NSFileManager defaultManager]
                     contentsOfDirectoryAtPath:
                     path
                     error:nil];
    NSString *thePath;
    NSString *fileName;
    for (int i=0;i<array.count;i++) {
        if ([[array objectAtIndex:i]hasSuffix:@".pfx"]) {
            fileName= [array objectAtIndex:i];
            thePath = [NSString stringWithFormat:@"%@/%@",path,fileName];
        }
    }

    SecIdentityRef identityApp = nil;

    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    NSString *pwd = Password;
    CFStringRef password = (CFStringRef)CFBridgingRetain(pwd);
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
    CFRelease(options);
    CFRelease(password);
    if (securityError == errSecSuccess) {
        NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
        SecCertificateRef certificate = NULL;
        SecIdentityCopyCertificate (identityApp, &certificate);

       OSStatus status = errSecSuccess;

        CFTypeRef persistent_ref = NULL;
        const void *keys[] = { kSecReturnPersistentRef, kSecValueRef };
        const void *values[] = { kCFBooleanTrue, identityApp };
        CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
                                                             2, NULL, NULL);
        status = SecItemAdd(dict, &persistent_ref);
        NSLog(@"Status %ld",status);

        if (dict)
        CFRelease(dict);
        [Utils deleteCertificateFromInboxFolder];
    } else {
        NSLog(@"Error opening Certificate.");
        [Utils displayAlertWithTitle:@"Wrong Password" andMessage:@"Error opening certificate"];
    }
}

Passing this credential in the authentication challenge

if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate)
    {
        fprintf(stderr, "identities:\n");
        fprintf(stderr, "certificates:\n");
        NSLog(@"NSURLAuthenticationMethodClientCertificate");

        SecIdentityRef identity;
        identity = (__bridge SecIdentityRef)([self _dumpCredentialsOfSecClass:kSecClassIdentity printSelector:@selector(_printIdentity:attributes:)]);
        SecCertificateRef certificate1 = NULL;
        OSStatus *stat = SecIdentityCopyCertificate (identity, &certificate1);
        //const void *certs[] = {certificate1};

        SecCertificateRef certArray[1] = { certificate1 };
        CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
        CFRelease(certificate1);
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity
                                                                 certificates:(__bridge NSArray *)myCerts
                                                                  persistence:NSURLCredentialPersistencePermanent];
        CFRelease(myCerts);

//        CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
//        
//        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistenceNone];
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}

App crashes once i pass this credential to the NSURLChallenge sender.

enter image description here

Any help is greatly appreciated!!!

Was it helpful?

Solution 2

Ok, i seem to have fixed it.

Changed

 identity = (__bridge SecIdentityRef)([self _dumpCredentialsOfSecClass:kSecClassIdentity printSelector:@selector(_printIdentity:attributes:)]);

to

identity = (SecIdentityRef)CFBridgingRetain([self _dumpCredentialsOfSecClass:kSecClassIdentity printSelector:@selector(_printIdentity:attributes:)]);

Hope this helps somebody

OTHER TIPS

Did you try to enable zombie object for your project? It may help you find out what has gone wrong.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top