Не удается создать соединение IPSEC с помощью NEVPNManager на iOS

Я пытаюсь создать VPN-подключение IPSEC в своем приложении для iOS.

Мой код для настройки конфигурации выглядит так:

-(void)setUpConfig
   NEVPNManager *manager = [NEVPNManager sharedManager];

    int status = manager.connection.status;

    if (status == NEVPNStatusConnected) {
        manager.connection stopVPNTunnel];
    } else {
        [manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
            NSError *startError;

        if (error) {
            NSLog(@"Load config failed [%@]", error.localizedDescription);
            return;
        }

        NEVPNProtocolIPSec *p = (NEVPNProtocolIPSec *)self.manager.protocol;
        if (!p) {
            p = [[NEVPNProtocolIPSec alloc] init];
        }

        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"base64_encoded_cert" ofType:@"txt"];
        NSString *certBase64String = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
        NSString *certPassword = @"cert_import_password";

        NSString *vpnUsername = @"myUsername";
        NSString *vpnPassword = @"myPassword";
        NSString *url = @"my.server.address";

        // This saves my credentials to the keychain and returns a persistent keychain reference
        NSData *passRef = [self addVPNCredentialsToKeychain:vpnUsername withPassword:vpnPassword];

        p.username = vpnUsername;
        p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
        p.serverAddress = url;
        p.passwordReference = passRef;
        p.identityData = [NSData dataWithBase64EncodedString:certBase64String];
        p.identityDataPassword = certPassword;
        p.disconnectOnSleep = NO;
        p.useExtendedAuthentication = YES;

        [manager setProtocol:p];
        [manager setOnDemandEnabled:NO];
        [manager setLocalizedDescription:@"My VPN"];

        [manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
            if(error) {
                NSLog(@"Save error: %@", error);
            } else {
                NSLog(@"Saved!");
                }
            }];
        }];
    }
}


-(NSData*)addVPNCredentialsToKeychain:(NSString*)username withPassword:(NSString*)password
{
    NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];

    NSData *encodedIdentifier = [username dataUsingEncoding:NSUTF8StringEncoding];

    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
    keychainItem[(__bridge id)kSecAttrDescription] = @"A password used to authenticate on a VPN server";
    keychainItem[(__bridge id)kSecAttrGeneric] = encodedIdentifier;
    keychainItem[(__bridge id)kSecAttrAccount] = encodedIdentifier;
    keychainItem[(__bridge id)kSecAttrService] = [[NSBundle mainBundle] bundleIdentifier];
    keychainItem[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
    keychainItem[(__bridge id)kSecReturnPersistentRef] = @YES;

    CFTypeRef typeResult = nil;

    OSStatus sts = SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, &typeResult);

    NSLog(@"Error Code: %d", (int)sts);

    if(sts == noErr) {
        NSData *theReference = (__bridge NSData *)typeResult;
    return theReference;

    } else {
        keychainItem[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding]; //Our password

        OSStatus sts = SecItemAdd((__bridge CFDictionaryRef)keychainItem, &typeResult);
        NSLog(@"Error Code: %d", (int)sts);

        NSData *theReference = (__bridge NSData *)(typeResult);
        return theReference;
    }
    return nil;
}

Затем я пытаюсь открыть VPN-соединение следующим образом:

-(void)connect
    NEVPNManager *manager = [NEVPNManager sharedManager];
    [manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
        NSError *startError;
        [manager.connection startVPNTunnelAndReturnError:&startError];

        if(startError) {
            NSLog(@"Start error: %@", startError.localizedDescription);
        }
    }];
}

И это действительно работает в большинстве случаев. Проблема, с которой я сталкиваюсь, заключается в том, что после восстановления заводских настроек устройства (конечно, на iOS 8) и попытки выполнить эту настройку и подключение мой профиль устанавливается нормально, но VPN не удается подключиться. На самом деле, моя интерпретация заключается в том, что попытка подключения не удалась.

После восстановления заводских настроек устройства и попытки подключения с использованием моего метода в журналах устройства появляется следующее:

<Notice>: NESMLegacySession[MyVPN:BB73C098-B22E-46D3-9491-2A6D9F559F8F]: Received a start command from VPNApp[256], but start was rejected

Вход в приложение «Настройки» и попытка вручную переключить VPN с помощью переключателя в разделе «Bluetooth» приводит к тому, что переключатель включается на долю секунды, а затем сразу же выключается. В этом случае создается следующий журнал:

<Warning>: -[VPNBundleController _vpnNetworkingIsDisabled]: Airplane mode: 0, WiFi Enabled: 1

В обоих случаях диалоговое окно об ошибке не создается, когда VPN не удается начать подключение — только журналы.

Я могу обойти эту проблему, выбрав «Настройки» > «Основные» > «VPN». После того, как я просто перешел на эту страницу (т. е. не переключал там VPN), я могу нормально управлять VPN. Даже переход на эту страницу до установки конфигурации VPN приводит к тому, что я могу нормально подключиться после установки конфигурации.

Моя цель - иметь возможность запускать VPN-соединение без необходимости сначала переходить на эту страницу VPN в настройках. Кто-нибудь может пролить свет на ситуацию? Мне кажется, что я что-то упускаю, чтобы сначала включить VPN-соединения.


person flyingtoaster0    schedule 19.10.2014    source источник


Ответы (1)


Это происходит из-за того, что конфигурация VPN оставалась disabled по умолчанию для первоначального VPN-подключения.

Вы должны включить VPN до saveToPreferencesWithCompletionHandler.

[[NEVPNManager sharedManager] setEnabled:YES];

Пример:

[[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler: ^(NSError *error) {
        if (error) {
            NSLog(@"Load error: %@", error);
        }
        else {
            // No errors! The rest of your codes goes here...
            NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
            p.serverAddress = @"VPN SERVER ADDRESS";
            p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
            p.localIdentifier = @"Local identifier";
            p.remoteIdentifier = @"Remote identifier";
            p.useExtendedAuthentication = YES;
            p.identityData = [NSData dataWithBase64EncodedString:certBase64String];;
            p.identityDataPassword = @"identity password";
            p.disconnectOnSleep = NO;
            
            // Set protocol
            [[NEVPNManager sharedManager] setProtocol:p];

            // Set on demand
            NSMutableArray *rules = [[NSMutableArray alloc] init];
            NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];
            [rules addObject:connectRule];
            [[NEVPNManager sharedManager] setOnDemandRules:rules];
            
            // Set localized description
            [[NEVPNManager sharedManager] setLocalizedDescription:@"Description"];
            
            // Enable VPN
            [[NEVPNManager sharedManager] setEnabled:YES];
            
            // Save to preference
            [[NEVPNManager sharedManager] saveToPreferencesWithCompletionHandler: ^(NSError *error) {
                NSLog(@"Save VPN to preference complete");
                if (error) {
                    NSLog(@"Save to preference error: %@", error);
                   
                }
            }];
        }
    }];
person Haris Dautović    schedule 27.10.2014