15

Our customers want to use an MDM (mobile device management) solution (MobileIron) to install client certificates onto corporate iOS devices, in order to limit access to certain corporate web services to corporate devices only.

MobileIron installs the client certificate into Settings > General > Profiles, which is the default location for certificates in iOS, and Safari can respond with this certificate when a corporate web service challenges it for one.

But I need the same thing to happen from within a custom app. When our app gets challenged for a certificate, I need to be able to respond with the certificate from Settings > General > Profiles. I have examples of responding with a certificate which is bundled with our app, and with a certificate which our app stores within its own keychain, but I do not have an example of responding with a certificate installed on the device in Settings > General > Profiles.

Can anyone explain to me more about what the NSURLAuthenticationChallengeSender protocol method -performDefaultHandlingForAuthenticationChallenge: does? Does default handling mean that iOS effectively responds to the challenge on behalf of the app? Can this response include a client certificate stored in Settings > General > Profiles?

Update

If the MDM could install a client certificate into the app keychain, that would be perfect.

4

3 回答 3

8

作为回应,Apple 技术支持向我指出了以下技术说明:

https://developer.apple.com/library/ios/qa/qa1745/_index.html

总而言之,不支持我们想要做的事情。

于 2014-09-23T08:11:57.267 回答
3

MobileIron 的AppConnect 2.1 更新解决了这个问题,无需特殊代码。X.509 证书可以使用 AppConnect 配置推送,并且 AppConnect 框架会在可以使用合格证书进行响应时拦截任何身份验证质询。证书可以在首次启动时即时创建、稍后撤销、为每个用户或每个设备定制,并且不同的 URL 可以使用不同的证书。

如果有人正在使用此页面上的代码片段,请停止,这不是必需的。包装未修改的应用程序或链接到 AppConnect 框架后,将 MI_AC_CLIENT_CERT_1 密钥添加到 AppConnect 配置,指向证书注册配置(即 SCEP、Entrust、Symantec PKI、PIV-D 等)。添加带有 URL 的 MI_AC_CLIENT_1_RULE 键(带有可选通配符)。没有第 3 步。您的应用现在将自动使用证书进行身份验证。

完整的详细信息在MobileIron 的 AppConnect 和 AppTunnel 指南文档中,在“从 AppConnect 应用程序到企业服务的证书身份验证”下。

于 2017-03-02T22:47:36.640 回答
1

我刚从一位使用 MobileIron 的客户那里回来,并希望这样做。MobileIron 开发支持为我们提供了这段代码,它通过 MobileIron 的核心配置技术导入 AppConnect Wrapper 提供的证书。

它不漂亮,但由于它是由他们提供的,所以我不允许修改它。它虽然有效!你把它插入到你的 AppDelegate.h 中:

- (NSString *)appConnectConfigChangedTo:(NSDictionary *)newConfig;

并将其放入您的 AppDelegate.m 中,就在前面的编译指示标记之后:

#pragma mark UIApplicationDelegate implementation                                
- (NSString *)appConnectConfigChangedTo:(NSDictionary *)newConfig{
        //NSLog(@"New config: %@", newConfig);                                          //unsecure
        NSLog(@"New config retrieved");                                                 //confirm we got a new config
        NSString *certStr       = [newConfig valueForKey:@"kUserCert"];                 //Store certificate as String
        NSString *certPassword  = [newConfig valueForKey:@"kUserCert_MI_CERT_PW"];      //Store certificate password as string
        NSData *cert = [[NSData alloc] initWithBase64EncodedString:certStr options:0];  //only for iOS7+, decodes base64 encoded certificate
        CFDataRef pkcs12Data = (__bridge CFDataRef)cert;                                //Extract identity & certificate objects from
        CFStringRef password = (__bridge CFStringRef)certPassword;                      //the cert data Identity
        SecIdentityRef myIdentity = nil;                                                //Initialize variable for identity
        SecCertificateRef myCertificate = nil;                                          //Initialize variable for certificate
        OSStatus status = extractIdentityAndTrust(pkcs12Data, password, &myIdentity, nil); //Use Apple-provided method for extracting Identity and Trust
        if (status != errSecSuccess || myIdentity == nil) { NSLog(@"Failed to extract identity and trust: %ld", status);} //Likely due to corruption
        else { SecIdentityCopyCertificate(myIdentity, &myCertificate); }                //This method is supposed to store the Certificate, but Fiori doesn't see it here
        const void *certs[] = { myCertificate };                                        //Initialize an array for one certificate
        CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);                    //Make the array the way Apple wants it to be
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistencePermanent];                                       //MobileIron's method of Credential storage
        NSMutableDictionary *secIdentityParams = [[NSMutableDictionary alloc] init];    //Initialize Dictionary to store identity
        [secIdentityParams setObject:(__bridge id)myIdentity forKey:(__bridge id)kSecValueRef]; //Build the secIdentityParams dictionary in the way the next method expects it to be
        OSStatus certInstallStatus = SecItemAdd((__bridge CFDictionaryRef) secIdentityParams, NULL); //Add the identity to the keychain for Fiori consumption
        if (myIdentity) CFRelease(myIdentity);                                          //Free
        if (certsArray) CFRelease(certsArray);                                          //Up
        if (myCertificate) CFRelease(myCertificate);                                    //Memory
        return nil;                                                                     //Success
} 
// Copied from Apple document on Certificates:
// http://developer.apple.com/library/mac/documentation/security/conceptual/CertKeyTrustProgGuide/CertKeyTrustProgGuide.pdf
OSStatus extractIdentityAndTrust(CFDataRef inP12data, CFStringRef password, SecIdentityRef *identity, SecTrustRef *trust){
        OSStatus securityError = errSecSuccess;
        const void *keys[] = { kSecImportExportPassphrase };
        const void *values[] = { password };
        CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
        CFArrayRef items = nil;
        securityError = SecPKCS12Import(inP12data, options, &items);
        if (securityError == errSecSuccess) {
                CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
                if (identity && CFDictionaryGetValueIfPresent(myIdentityAndTrust, kSecImportItemIdentity, (const void **)identity)) {
                        CFRetain(*identity);
                    }
                if (trust && CFDictionaryGetValueIfPresent(myIdentityAndTrust, kSecImportItemTrust, (const void **)trust)) {
                        CFRetain(*trust);
                    }
            }  
        if (options) {CFRelease(options);}
        if (items) {CFRelease(items);}
        return securityError;
}

构建应用程序后,请 MobileIron 管理员“打包”应用程序,以便它可以使用 AppConnect。完成此操作并部署打包的应用程序以通过 MobileIron 测试用户后,设置一个核心配置,该核心配置采用特定于已配置用户的用户证书,并将其推送到核心配置密钥“kUserCert”下的已配置设备。

于 2015-10-27T14:55:26.663 回答