【问题标题】:iOS Client Certificates and Mobile Device ManagementiOS 客户端证书和移动设备管理
【发布时间】:2014-11-13 12:21:41
【问题描述】:

我们的客户希望使用 MDM(移动设备管理)解决方案 (MobileIron) 将客户端证书安装到企业 iOS 设备上,以便将某些企业网络服务的访问权限仅限于企业设备。

MobileIron 将客户端证书安装到 Settings > General > Profiles,这是 iOS 中证书的默认位置,当企业网络服务质疑它时,Safari 可以使用此证书做出响应。

但我需要在自定义应用程序中发生同样的事情。当我们的应用程序受到证书的挑战时,我需要能够从 Settings > General > Profiles 中使用证书进行响应。我有使用与我们的应用程序捆绑在一起的证书以及我们的应用程序存储在其自己的钥匙串中的证书进行响应的示例,但我没有有使用安装在应用程序上的证书进行响应的示例设置 > 常规 > 配置文件中的设备。

谁能向我解释一下NSURLAuthenticationChallengeSender 协议方法-performDefaultHandlingForAuthenticationChallenge: 的作用? 默认处理是否意味着 iOS 代表应用程序有效地响应了挑战?此响应是否可以包含存储在 Settings > General > Profiles 中的客户端证书?

更新

如果 MDM 可以将客户端证书安装到应用钥匙串中,那就完美了。

【问题讨论】:

    标签: ios ssl client-certificates mobileiron


    【解决方案1】:

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

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

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

    【讨论】:

    • 好的,所以从促使我提​​出这个问题的项目继续前进,我无法尝试这个。听起来它可以满足客户的需求,并且可以简化我们的应用程序。不过,“拦截身份验证挑战”部分让我有些困扰。这是否意味着 AppConnect 成为 HTTPS 端点,而不是应用程序?所以 AppConnect 代理流量?
    • Brett,我不会在这里透露我们的“秘诀”,但请查看 developer.apple.com/library/content/documentation/Cocoa/… 和 NSURLAuthenticationMethodClientCertificate。
    • 我认为这是“是”。
    • 我猜这适用于较新的会话,因为它们是共享的,因此您的框架可以拦截它们。
    【解决方案2】:

    我刚从一位正在使用 MobileIron 的客户那里回来,并希望这样做。 MobileIron 开发支持为我们提供了这个 sn-p 代码,它通过 MobileIron 的 Core Config 技术导入 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”下的已配置设备。

    【讨论】:

    • 非常有趣。必须尝试获取 MobileIron 的副本才能尝试。
    • 是否需要与 MobileIron SDK 集成?
    • 对于这个功能,没有传统的库或 SDK:MobileIron 为我们提供了示例代码,我们必须使用他们的 Wrapper 应用程序来包装应用程序以使 AppConnect 工作。
    • @LewWeiHao 它必须来自 MobileIron
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-30
      • 1970-01-01
      • 1970-01-01
      • 2020-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多