iOS Client Certificates and Mobile Device Management

断了今生、忘了曾经 提交于 2019-11-27 21:24:33

Apple tech support pointed me to the following tech note in response:

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

To summarise, what we want to do is not supported.

MobileIron's AppConnect 2.1 update solves this problem, no special code required. X.509 certificates can be pushed with the AppConnect Configuration, and the AppConnect framework intercepts any authentication challenges when it can respond with an eligible cert. Certs can be created on-the-fly at first launch, revoked later, customized per-user or per-device, and different URL's can use different certs.

If anyone is using the code snippet on this page, stop, it's not needed. After wrapping your unmodified app, or linking-in the AppConnect framework, add an MI_AC_CLIENT_CERT_1 key to your AppConnect Configuration, pointing to a Certificate Enrollment configuration (i.e. SCEP, Entrust, Symantec PKI, PIV-D, etc.). Add a MI_AC_CLIENT_1_RULE key with a URL (with optional wildcard). There is no step 3. Your app will now automatically use certs for authentication.

Full details are in MobileIron's AppConnect and AppTunnel Guide docs, under "Certificate authentication from AppConnect apps to enterprise services".

I just came back from an onsite at a customer who was using MobileIron and was looking to do just this. MobileIron development support provided us with this snippet of code, which imports a certificate provided by the AppConnect Wrapper through MobileIron's Core Config technology.

It's not pretty, but as it was provided by them I was not allowed to modify it. It works though! You insert this into your AppDelegate.h:

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

And this into your AppDelegate.m, right after the aforemention 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;
}

Once you've built your app, ask the MobileIron administrator to 'wrap' the app so that it can use AppConnect. Once that is done and the wrapped app is deployed to test users via MobileIron, set up a Core Config that take a User Certificate specific for the provisioned user and pushes it to the provisioned devices under the Core Config key "kUserCert".

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!