I'm trying to create an IPSEC VPN connection in my iOS app.
My code for setting up the configuration looks like this:
-(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;
}
I then attempt to open a VPN connection like so:
-(void)connect
NEVPNManager *manager = [NEVPNManager sharedManager];
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
NSError *startError;
[manager.connection startVPNTunnelAndReturnError:&startError];
if(startError) {
NSLog(@"Start error: %@", startError.localizedDescription);
}
}];
}
And this does work under most conditions. The problem I am experiencing is that after factory restoring a device (on iOS 8, of course), and attempting to go through this setup and connection, my profile installs just fine, but the VPN fails to connect. In fact, my interpretation is that it is failing to attempt to connect.
After factory restoring a device and attempting to connect using my method, the following shows up in the device logs:
<Notice>: NESMLegacySession[MyVPN:BB73C098-B22E-46D3-9491-2A6D9F559F8F]: Received a start command from VPNApp[256], but start was rejected
Going into the Settings app and attempting to manually toggle the VPN using the switch under "Bluetooth" results in the switch turning on for a split second and then immediately going by to off. In this case, the following log is produced:
<Warning>: -[VPNBundleController _vpnNetworkingIsDisabled]: Airplane mode: 0, WiFi Enabled: 1
In both cases, no error dialog is produced when the VPN fails to start connecting - just the logs.
I can get around this problem by navigating to Settings > General > VPN. Once having just gone to that page (i.e. Not toggling VPN there), I can then control VPN just fine. Even going to that page before a VPN configuration is even installed results in me being able to connect just fine after installing a configuration.
My goal is to be able to start the VPN connection without having to first go to that VPN page in Settings. Can anyone shed some light on the situation? It seems to me like I'm missing something to first enable VPN connections.
With the Personal VPN feature in macOS and iOS, your app can create and manage a VPN configuration that uses one of the built-in VPN protocols (IPsec or IKEv2). The user must explicitly authorize your app the first time it saves a VPN configuration.
Android includes a built-in (PPTPPPTPThe Point-to-Point Tunneling Protocol (PPTP) is an obsolete method for implementing virtual private networks. PPTP has many well known security issues. PPTP uses a TCP control channel and a Generic Routing Encapsulation tunnel to encapsulate PPP packets.https://en.wikipedia.org › Point-to-Point_Tunneling_ProtocolPoint-to-Point Tunneling Protocol - Wikipedia, L2TP/IPSec, and IPSec) VPN client. Devices running Android 4.0 and later also support VPN apps.
This appears because VPN configuration stayed disabled
by default for initial VPN connection.
You must enable VPN before saveToPreferencesWithCompletionHandler
.
[[NEVPNManager sharedManager] setEnabled:YES];
Example:
[[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);
}
}];
}
}];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With