Не удается создать соединение 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-соединения.
1 ответ
У меня такая же проблема. Это происходит потому, что ваш VPN Config был отключен по умолчанию.
Вам необходимо установить:
[[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);
}
}];
}
}];