iOS客户端证书和移动设备管理

16
我们的客户想要使用MDM(移动设备管理)解决方案(MobileIron)将客户端证书安装到企业iOS设备上,以限制对某些公司Web服务的访问仅限于公司设备。
MobileIron将客户端证书安装到“设置>常规>个人资料”中,这是iOS中证书的默认位置。当公司Web服务向其请求证书时,Safari可以用此证书做出响应。
但我需要从自定义应用程序内部发生相同的情况。当我们的应用程序受到证书挑战时,我需要能够从“设置>常规>个人资料”中响应证书。我有响应与应用程序捆绑在一起的证书和响应存储在其自己的密钥链中的证书的示例,但我没有响应在“设置>常规>个人资料”中安装的证书的示例。
有人能否向我解释一下NSURLAuthenticationChallengeSender协议方法-performDefaultHandlingForAuthenticationChallenge:做了些什么?默认处理是否意味着iOS代表应用程序响应挑战?此响应能否包括存储在“设置>常规>个人资料”中的客户端证书?
更新
如果MDM能够将客户端证书安装到应用程序密钥链中,那就太完美了。
3个回答

8

3
MobileIron的AppConnect 2.1更新解决了这个问题,无需特殊代码。可以通过AppConnect配置推送X.509证书,并且当AppConnect框架能够使用合格证书响应任何身份验证挑战时,它会拦截它们。证书可以在首次启动时即时创建,稍后撤销,按用户或设备定制,并且不同的URL可以使用不同的证书。
如果有人在使用此页面上的代码片段,请停止使用,因为它不是必要的。在包装未修改的应用程序或链接AppConnect框架之后,在您的AppConnect配置中添加一个MI_AC_CLIENT_CERT_1密钥,指向证书注册配置(即SCEP、Entrust、Symantec PKI、PIV-D等)。添加一个带有URL(可选通配符)的MI_AC_CLIENT_1_RULE密钥。没有第三步。现在您的应用程序将自动使用证书进行身份验证。
完整的细节在MobileIron的AppConnect和AppTunnel指南文档中,位于“从AppConnect应用到企业服务的证书认证”下。

好的,那么我已经开始着手于促使我提出这个问题的项目。我现在无法尝试这个方法。听起来它会满足客户的需求并简化我们的应用程序。然而,“截断认证挑战”部分让我有点担心。这是不是意味着AppConnect成为HTTPS端点,而不是应用程序?所以AppConnect代理流量? - Brett Donald
Brett,我不会在这里泄露我们的“秘密配方”,但请查看https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html和NSURLAuthenticationMethodClientCertificate。 - Cliff Tuel
我会把这理解为“是的”。 - Brett Donald
我猜这应该可以与新的会话一起使用,因为它们是共享的,所以你的框架可以拦截它们。 - Stephen J

1
我刚刚从一家客户的现场回来,他们正在使用MobileIron并希望实现这一点。MobileIron开发支持向我们提供了这个代码片段,它通过MobileIron的Core Config技术导入了AppConnect Wrapper提供的证书。
虽然它看起来不太美观,但由于是他们提供的,所以我不能修改它。不过它确实有效!您可以将其插入到您的AppDelegate.h文件中:
- (NSString *)appConnectConfigChangedTo:(NSDictionary *)newConfig;

在AppDelegate.m中加入以下内容,放在之前提到的pragma mark之后:
#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”下。

非常有趣。我们必须尝试获取MobileIron的副本来尝试一下。 - Brett Donald
您是否需要集成MobileIron SDK? - windfly2006
针对这个功能,没有传统的库或SDK可用:MobileIron向我们提供了示例代码,我们必须使用他们的包装应用程序来包装应用程序以使AppConnect正常工作。 - Jarek Lupinski
@LewWeiHao 这需要来自MobileIron。 - Jarek Lupinski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接