How should I implement iOS app’s WKWebView using the TLS client certificate?

Hello everyone,


Trying to implement the client certificate authentication on iOS app’s WKWebVIew, but it does not work.

In particular, somehow didReceiveAuthenticationChallenge delegete is called twice in one request.

First, authenticationMethod is NSURLAuthenticationMethodClientCertificate, then at second time, authenticationMethod is NSURLAuthenticationMethodServerTrust. And finally didFinishNavigation delegete gets a error “The server “www.example.com” requires a client certificate.”


My code (abstract) is below.

Is there anyone who has some idea? Thanks in advance for your help.


- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURL *url = [NSURL URLWithString:URL];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
    NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];

    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) // called 1st
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *DocumentsDirPath = [paths objectAtIndex:0];
        NSString *certName = @"clientcert.p12";
        NSString *certPath = [NSString stringWithFormat:@"%@/%@",DocumentsDirPath,certName];
        NSString *certPass = @"certpassword";
        SecIdentityRef myIdentity = [self getClientCertificate:certPath password:certPass]; // definition will appear at the end
  
        SecCertificateRef certificateRef;
        SecIdentityCopyCertificate(myIdentity, &certificateRef);
        SecCertificateRef certs[1] = { certificateRef };
        CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certs, 1, NULL);
  
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity
                                                                 certificates:(__bridge NSArray *)myCerts
                                                                  persistence:NSURLCredentialPersistencePermanent];
  
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    }
    else if([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) // called 2nd
    {
        SecTrustRef secTrustRef = challenge.protectionSpace.serverTrust;
        if (secTrustRef != NULL)
        {
            SecTrustResultType result;
            OSErr er = SecTrustEvaluate(secTrustRef, &result);
            if (er != noErr){
                NSLog(@"error");
            }
      
            switch ( result )
            {
                case kSecTrustResultProceed:
                    NSLog(@"kSecTrustResultProceed");
                    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
                    break;
                case kSecTrustResultUnspecified: // called 2nd
                    NSLog(@"kSecTrustResultUnspecified");
                    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:secTrustRef]);
                    break;
                case kSecTrustResultRecoverableTrustFailure:
                    NSLog(@"kSecTrustResultRecoverableTrustFailure");
                    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:secTrustRef]);
                    break;
            }
        }
    } else {
        NSLog(@"else");
    }
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    if (!error){
        NSLog(@"didFailProvisionalNavigation Succeeded.");
  
    } else {
        NSString *message = [error localizedDescription];
        NSLog(@"didFailProvisionalNavigation Failed. message: %@", message); // The server “www.example.com” requires a client certificate.
    }
}

- (SecIdentityRef)getClientCertificate:(NSString *)path password:(NSString *)pass
{
    SecCertificateRef identityApp = nil;
    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:path];
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    CFStringRef password = (__bridge CFStringRef)pass;
    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);
    if (securityError == errSecSuccess) {
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        identityApp = (SecCertificateRef)CFDictionaryGetValue(identityDict, kSecImportItemCertChain);
    } else {
        // ERROR
    }
    return identityApp;
}

I am having a similar issue. Have you found a solution?

I have the same problem, but steps are inverted. First I get a NSURLAuthenticationMethodServerTrust and after I get a NSURLAuthenticationMethodClientCertificate but still not working.


Regards

We are having the exact same issue with our certificate credential not being sufficient. The same pattern works fine for UIWebView, but when we try the same pattern for a WKWebView, the server does not receive/accept the credential.

Sounds like an issue with WKWebView: https://forums.developer.apple.com/thread/67651

How should I implement iOS app’s WKWebView using the TLS client certificate?
 
 
Q