Сертификаты клиента iOS и управление мобильными устройствами
Наши клиенты хотят использовать решение MDM (управление мобильными устройствами) (MobileIron) для установки клиентских сертификатов на корпоративные устройства iOS, чтобы ограничить доступ к определенным корпоративным веб-службам только для корпоративных устройств.
MobileIron устанавливает сертификат клиента в " Настройки"> "Основные"> "Профили", который является местоположением по умолчанию для сертификатов в iOS, и Safari может ответить этим сертификатом, если корпоративная веб-служба его оспаривает.
Но мне нужно, чтобы то же самое происходило в приложении. Когда у нашего приложения возникает проблема с сертификатом, мне нужно иметь возможность ответить сертификатом в меню "Настройки"> "Основные"> "Профили". У меня есть примеры ответов с помощью сертификата, который связан с нашим приложением, и с сертификатом, который наше приложение хранит в собственной цепочке для ключей, но у меня нет примера ответа с сертификатом, установленным на устройстве, в Настройки> Общие> Профили,
Может кто-нибудь объяснить мне больше о том, что NSURLAuthenticationChallengeSender
метод протокола -performDefaultHandlingForAuthenticationChallenge:
делает? Означает ли обработка по умолчанию, что iOS эффективно отвечает на вызов от имени приложения? Может ли этот ответ включать сертификат клиента, хранящийся в меню "Настройки"> "Основные"> "Профили"?
Обновить
Если бы MDM мог установить сертификат клиента в связку ключей приложения, это было бы прекрасно.
3 ответа
Техническая поддержка Apple указала мне на следующую техническую заметку:
https://developer.apple.com/library/ios/qa/qa1745/_index.html
Подводя итог, то, что мы хотим сделать, не поддерживается.
Обновление AppConnect 2.1 MobileIron решает эту проблему, специальный код не требуется. Сертификаты X.509 могут быть отправлены с помощью Конфигурации AppConnect, и платформа AppConnect перехватывает любые проблемы аутентификации, когда она может ответить с приемлемым сертификатом. Сертификаты могут создаваться "на лету" при первом запуске, позже аннулироваться, настраиваться для каждого пользователя или устройства, и разные URL-адреса могут использовать разные сертификаты.
Если кто-то использует фрагмент кода на этой странице, остановитесь, он не нужен. После обертывания вашего неизмененного приложения или связывания в платформе AppConnect добавьте ключ MI_AC_CLIENT_CERT_1 в свою конфигурацию AppConnect, указывая на конфигурацию регистрации сертификата (т. Е. SCEP, Entrust, Symantec PKI, PIV-D и т. Д.). Добавьте ключ MI_AC_CLIENT_1_RULE с URL-адресом (с необязательным подстановочным знаком). Нет шага 3. Ваше приложение теперь будет автоматически использовать сертификаты для аутентификации.
Полная информация содержится в документах MobileConv и AppTunnel Guide, в разделе "Проверка подлинности сертификата от приложений AppConnect до корпоративных сервисов".
Я только что вернулся с места у клиента, который использовал MobileIron и хотел сделать именно это. Поддержка разработки MobileIron предоставила нам этот фрагмент кода, который импортирует сертификат, предоставленный оболочкой AppConnect Wrapper, с помощью технологии Core Config MobileIron.
Это не красиво, но, как это было предоставлено ими, мне не разрешили его изменить. Это работает, хотя! Вы вставляете это в свой 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, настройте Core Config, который принимает сертификат пользователя, специфичный для предоставленного пользователя, и передает его на подготовленные устройства под ключом Core Config "kUserCert".