简体   繁体   English

在企业 iOS 应用程序中使用来自 .mobileconfig 的客户端 SSL 证书

[英]Using Client SSL Certificate from .mobileconfig within Enterprise iOS app

We're trying to use Client SSL certificates for user authentication in an enterprise iOS app.我们正在尝试在企业 iOS 应用程序中使用客户端 SSL 证书进行用户身份验证。

  • We can generate the client ssl cert on the server我们可以在服务器上生成客户端ssl证书
  • The user can install this through a .mobileconfig用户可以通过 .mobileconfig 安装它
  • Authentication to a web server in Safari works with the installed cert. Safari 中对 Web 服务器的身份验证与已安装的证书一起使用。
  • Making an http request from inside an iOS app fails (the certificate is not used).从 iOS 应用程序内部发出 http 请求失败(未使用证书)。

How do we get this to work?我们如何让它发挥作用? Thanks!谢谢!

Overview:概述:

You have installed the Client SSL Certificate on the device keychain.您已在设备钥匙串上安装了客户端 SSL 证书。

Safari.app and Mail.app has access to this keychain while the iOS app doesn't. Safari.app 和 Mail.app 可以访问此钥匙串,而 iOS 应用程序则不能。

The reason is the apps that we develop are sandboxed and doesn't have any access rights outside of it in the non-jailbroken device.原因是我们开发的应用程序是沙盒的,在非越狱设备中没有任何访问权限。

As safari has access to it,it had no trouble connecting and authenticating against the server challenge.由于 safari 可以访问它,因此连接和验证服务器挑战没有问题。

Solution:解决方案:

Include the exported P12 file with the App bundle and refer to it to find the correct client certificate the server was looking for.It is actually a workaround.将导出的 P12 文件包含在 App 包中,并参考它以找到服务器正在寻找的正确客户端证书。这实际上是一种解决方法。 The hardcoding is the reliable way to grab the P12 file.硬编码是获取 P12 文件的可靠方法。

Implementation:执行:

Method in question is willSendRequestForAuthenticationChallenge in NSURLConenction delegate .有问题的方法是NSURLConenction delegate willSendRequestForAuthenticationChallenge You need to account for NSURLAuthenticationMethodClientCertificate challenge type inorder to handle the server challenge.您需要考虑NSURLAuthenticationMethodClientCertificate质询类型以处理服务器质询。 This is where we implemented the magic to extract the correct certificate identity from the embedded P12 file.这是我们实现从嵌入的 P12 文件中提取正确证书身份的魔法的地方。 Code is below代码如下

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if ([challenge previousFailureCount] > 0) {
       //this will cause an authentication failure
       [[challenge sender] cancelAuthenticationChallenge:challenge];
       NSLog(@"Bad Username Or Password");        
       return;
    }



     //this is checking the server certificate
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            SecTrustResultType result;
            //This takes the serverTrust object and checkes it against your keychain
            SecTrustEvaluate(challenge.protectionSpace.serverTrust, &result);

            //if we want to ignore invalid server for certificates, we just accept the server
            if (kSPAllowInvalidServerCertificates) {
                [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
                return;
            } else if(result == kSecTrustResultProceed || result == kSecTrustResultConfirm ||  result == kSecTrustResultUnspecified) {
                //When testing this against a trusted server I got kSecTrustResultUnspecified every time. But the other two match the description of a trusted server
                [challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
                return;
            }
        } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
        //this handles authenticating the client certificate

       /* 
 What we need to do here is get the certificate and an an identity so we can do this:
   NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:myCerts persistence:NSURLCredentialPersistencePermanent];
   [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];

   It's easy to load the certificate using the code in -installCertificate
   It's more difficult to get the identity.
   We can get it from a .p12 file, but you need a passphrase:
   */

   NSString *p12Path = [[BundleManager bundleForCurrentSkin] pathForResource:kP12FileName ofType:@"p12"];
   NSData *p12Data = [[NSData alloc] initWithContentsOfFile:p12Path];

   CFStringRef password = CFSTR("PASSWORD");
   const void *keys[] = { kSecImportExportPassphrase };
   const void *values[] = { password };
   CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
   CFArrayRef p12Items;

   OSStatus result = SecPKCS12Import((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:(NSArray *)myCerts persistence:NSURLCredentialPersistencePermanent];
             CFRelease(myCerts);

             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
         }
    } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodDefault || [[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodNTLM) {
   // For normal authentication based on username and password. This could be NTLM or Default.

        DAVCredentials *cred = _parentSession.credentials;
        NSURLCredential *credential = [NSURLCredential credentialWithUser:cred.username password:cred.password persistence:NSURLCredentialPersistenceForSession];
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    } else {
        //If everything fails, we cancel the challenge.
        [[challenge sender] cancelAuthenticationChallenge:challenge];
    }
}

Reference: Ref1 , Ref2 , Ref3参考: Ref1Ref2Ref3

Hope this helps希望这可以帮助

I created a package to work with TLS sockets/iOS/Obj-C.我创建了一个包来处理 TLS 套接字/iOS/Obj-C。 I connect it to a node server but it might work with others.我将它连接到节点服务器,但它可能与其他人一起工作。 More importantly, I have links to important resources regarding creating certificates correctly given the recent iOS 13 TLS restrictions.更重要的是,鉴于最近的 iOS 13 TLS 限制,我提供了有关正确创建证书的重要资源的链接。 I hope it can be somewhat helpful:我希望它可以有点帮助:

https://github.com/eamonwhiter73/IOSObjCWebSockets/tree/master https://github.com/eamonwhiter73/IOSObjCWebSockets/tree/master

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM