We needed to pro grammatically start an IPSec VPN connection from an App we are working on. Luckily Apple is providing a nice set API for doing that without external library.
To create a VPN connection in iOS you do not need to obtain a network extension entitlement from apple.
Create a VPN
import NetworkExtension
class VPN {
let vpnManager = NEVPNManager.shared();
private var vpnLoadHandler: (Error?) -> Void { return
{ (error:Error?) in
if ((error) != nil) {
print("Could not load VPN Configurations")
return;
}
let p = NEVPNProtocolIPSec()
p.username = "SOME_USERNAME"
p.serverAddress = "example.com"
p.authenticationMethod = NEVPNIKEAuthenticationMethod.sharedSecret
let kcs = KeychainService();
kcs.save(key: "SHARED", value: "MY_SHARED_KEY")
kcs.save(key: "VPN_PASSWORD", value: "MY_PASSWORD"
p.sharedSecretReference = kcs.load(key: "SHARED")
p.passwordReference = kcs.load(key: "VPN_PASSWORD)
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
self.vpnManager.protocolConfiguration = p
self.vpnManager.localizedDescription = "Contensi"
self.vpnManager.isEnabled = true
self.vpnManager.saveToPreferences(completionHandler: self.vpnSaveHandler)
} }
private var vpnSaveHandler: (Error?) -> Void { return
{ (error:Error?) in
if (error != nil) {
print("Could not save VPN Configurations")
return
} else {
do {
try self.vpnManager.connection.startVPNTunnel()
} catch let error {
print("Error starting VPN Connection \(error.localizedDescription)");
}
}
}
self.vpnlock = false
}}
public func connectVPN() {
//For no known reason the process of saving/loading the VPN configurations fails.On the 2nd time it works
do {
try self.vpnManager.loadFromPreferences(completionHandler: self.vpnLoadHandler)
} catch let error {
print("Could not start VPN Connection: \(error.localizedDescription)" )
}
}
public func disconnectVPN() ->Void {
vpnManager.connection.stopVPNTunnel()
}
}
The code does the following:
*
Load the preferences
*
Change the preferences to the desired values (username, password, URL etc..)
*
Save the preferences
*
Start the connection
I know that it is weird to load the preferences, and then save it, although we didn't have anything to load. But this is unfortunately how Apple decided that it should be done. If you apply your changes directly and saved to preferences before loading preferences, the save operation will fail. *sighs*
The password and the shared key have to be saved in a specific key chain. You can read more about saving/loading keychain entries for VPN in this other blog post.
Also note that the loadFromPreferences
and the saveToPreferences
callbacks are asynchronous
One more thing I learnt while figuring out this API is that the call vpnManager.connection.startVPNTunnel()
succeeds it does not mean the VPN connection has been established successfully, but it means that the process of establishing a VPN tunnel has been started successfully. Apparently Apple has been writing those APIs for lawyers.
Finally if you want to be notified when the VPN connection has been established successfully, or otherwise has been disconnected, you need to use the NotificationCenter
class. I'll describe this in a separate post.