I am trying to create VPN connection in my app. I go through this link
https://developer.apple.com/documentation/networkextension/nevpnmanager
but did not find any official code to use NEVPNManager and even not found any tutorial to use this NEVPNManager.
I am new to VPN concept and don't know that much about it. so Can anyone give some solutions?
Here is a IKEv2 configuration of VPN using Network Extension (Without shared key and certificate) in Swift 4.2:
final class VPNHandler {
let vpnManager = NEVPNManager.shared()
func initVPNTunnelProviderManager() {
print("CALL LOAD TO PREFERENCES...")
self.vpnManager.loadFromPreferences { (error) -> Void in
if((error) != nil) {
print("VPN Preferences error: 1")
} else {
let IKEv2Protocol = NEVPNProtocolIKEv2()
IKEv2Protocol.username = vpnUser.username
IKEv2Protocol.serverAddress = vpnServer.serverID //server tunneling Address
IKEv2Protocol.remoteIdentifier = vpnServer.remoteID //Remote id
IKEv2Protocol.localIdentifier = vpnUser.localID //Local id
IKEv2Protocol.deadPeerDetectionRate = .low
IKEv2Protocol.authenticationMethod = .none
IKEv2Protocol.useExtendedAuthentication = true //if you are using sharedSecret method then make it false
IKEv2Protocol.disconnectOnSleep = false
//Set IKE SA (Security Association) Params...
IKEv2Protocol.ikeSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES256
IKEv2Protocol.ikeSecurityAssociationParameters.integrityAlgorithm = .SHA256
IKEv2Protocol.ikeSecurityAssociationParameters.diffieHellmanGroup = .group14
IKEv2Protocol.ikeSecurityAssociationParameters.lifetimeMinutes = 1440
//IKEv2Protocol.ikeSecurityAssociationParameters.isProxy() = false
//Set CHILD SA (Security Association) Params...
IKEv2Protocol.childSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES256
IKEv2Protocol.childSecurityAssociationParameters.integrityAlgorithm = .SHA256
IKEv2Protocol.childSecurityAssociationParameters.diffieHellmanGroup = .group14
IKEv2Protocol.childSecurityAssociationParameters.lifetimeMinutes = 1440
let kcs = KeychainService()
//Save password in keychain...
kcs.save(key: "VPN_PASSWORD", value: vpnUser.password)
//Load password from keychain...
IKEv2Protocol.passwordReference = kcs.load(key: "VPN_PASSWORD")
self.vpnManager.protocolConfiguration = IKEv2Protocol
self.vpnManager.localizedDescription = "Safe Login Configuration"
self.vpnManager.isEnabled = true
self.vpnManager.isOnDemandEnabled = true
//print(IKEv2Protocol)
//Set rules
var rules = [NEOnDemandRule]()
let rule = NEOnDemandRuleConnect()
rule.interfaceTypeMatch = .any
rules.append(rule)
print("SAVE TO PREFERENCES...")
//SAVE TO PREFERENCES...
self.vpnManager.saveToPreferences(completionHandler: { (error) -> Void in
if((error) != nil) {
print("VPN Preferences error: 2")
} else {
print("CALL LOAD TO PREFERENCES AGAIN...")
//CALL LOAD TO PREFERENCES AGAIN...
self.vpnManager.loadFromPreferences(completionHandler: { (error) in
if ((error) != nil) {
print("VPN Preferences error: 2")
} else {
var startError: NSError?
do {
//START THE CONNECTION...
try self.vpnManager.connection.startVPNTunnel()
} catch let error as NSError {
startError = error
print(startError.debugDescription)
} catch {
print("Fatal Error")
fatalError()
}
if ((startError) != nil) {
print("VPN Preferences error: 3")
//Show alert here
print("title: Oops.., message: Something went wrong while connecting to the VPN. Please try again.")
print(startError.debugDescription)
} else {
//self.VPNStatusDidChange(nil)
print("Starting VPN...")
}
}
})
}
})
}
} //END OF .loadFromPreferences //
}
//MARK:- Connect VPN
static func connectVPN() {
VPNHandler().initVPNTunnelProviderManager()
}
//MARK:- Disconnect VPN
static func disconnectVPN() {
VPNHandler().vpnManager.connection.stopVPNTunnel()
}
//MARK:- check connection staatus
static func checkStatus() {
let status = VPNHandler().vpnManager.connection.status
print("VPN connection status = \(status.rawValue)")
switch status {
case NEVPNStatus.connected:
print("Connected")
case NEVPNStatus.invalid, NEVPNStatus.disconnected :
print("Disconnected")
case NEVPNStatus.connecting , NEVPNStatus.reasserting:
print("Connecting")
case NEVPNStatus.disconnecting:
print("Disconnecting")
default:
print("Unknown VPN connection status")
}
}
}
Code for keychain:
//MARK:- Variables for keychain access
// Identifiers
let serviceIdentifier = "MySerivice"
let userAccount = "authenticatedUser"
let accessGroup = "MySerivice"
// Arguments for the keychain queries
var kSecAttrAccessGroupSwift = NSString(format: kSecClass)
let kSecClassValue = kSecClass as CFString
let kSecAttrAccountValue = kSecAttrAccount as CFString
let kSecValueDataValue = kSecValueData as CFString
let kSecClassGenericPasswordValue = kSecClassGenericPassword as CFString
let kSecAttrServiceValue = kSecAttrService as CFString
let kSecMatchLimitValue = kSecMatchLimit as CFString
let kSecReturnDataValue = kSecReturnData as CFString
let kSecMatchLimitOneValue = kSecMatchLimitOne as CFString
let kSecAttrGenericValue = kSecAttrGeneric as CFString
let kSecAttrAccessibleValue = kSecAttrAccessible as CFString
class KeychainService: NSObject {
func save(key:String, value:String) {
let keyData: Data = key.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let valueData: Data = value.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let keychainQuery = NSMutableDictionary();
keychainQuery[kSecClassValue as! NSCopying] = kSecClassGenericPasswordValue
keychainQuery[kSecAttrGenericValue as! NSCopying] = keyData
keychainQuery[kSecAttrAccountValue as! NSCopying] = keyData
keychainQuery[kSecAttrServiceValue as! NSCopying] = "VPN"
keychainQuery[kSecAttrAccessibleValue as! NSCopying] = kSecAttrAccessibleAlwaysThisDeviceOnly
keychainQuery[kSecValueData as! NSCopying] = valueData;
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionary)
SecItemAdd(keychainQuery as CFDictionary, nil)
}
func load(key: String)->Data {
let keyData: Data = key.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)!
let keychainQuery = NSMutableDictionary();
keychainQuery[kSecClassValue as! NSCopying] = kSecClassGenericPasswordValue
keychainQuery[kSecAttrGenericValue as! NSCopying] = keyData
keychainQuery[kSecAttrAccountValue as! NSCopying] = keyData
keychainQuery[kSecAttrServiceValue as! NSCopying] = "VPN"
keychainQuery[kSecAttrAccessibleValue as! NSCopying] = kSecAttrAccessibleAlwaysThisDeviceOnly
keychainQuery[kSecMatchLimit] = kSecMatchLimitOne
keychainQuery[kSecReturnPersistentRef] = kCFBooleanTrue
var result: AnyObject?
let status = withUnsafeMutablePointer(to: &result) { SecItemCopyMatching(keychainQuery, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let data = result as! NSData? {
if let value = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue) {
//print(value)
}
return data as Data;
}
}
return "".data(using: .utf8)!;
}
}
This tutorial help me to create VPN connection.
http://blog.moatazthenervous.com/create-a-vpn-connection-with-apple-swift/
VPN file for connection and disconnection
import Foundation
import NetworkExtension
// MARK: - NEVPNManager
// MARK: -
private var vpnLoadHandler: (Error?) -> Void { return
{ (error:Error?) in
if ((error) != nil) {
print("Could not load VPN Configurations")
self.removeToast()
return;
}
self.showToast(msg: STRINGVALUES.kCreatingConnection)
//VPN connection via Username password
let p = NEVPNProtocolIPSec()
let kcs = KeychainService()
p.authenticationMethod = NEVPNIKEAuthenticationMethod.sharedSecret
//For the security purpose added word xyz in password .so it should be remove while connecting
if self.selectedSever != nil{
self.selectedSever?.password = (self.selectedSever?.password.replacingOccurrences(of: "xyz", with: ""))!
p.username = self.selectedSever?.userName
p.serverAddress = self.selectedSever?.serverAddress
kcs.save(key: "SHARED", value: (self.selectedSever?.password)!)
kcs.save(key: "VPN_PASSWORD", value: (self.selectedSever?.password)!)
p.sharedSecretReference = kcs.load(key: STRINGVALUES.kShared)
p.passwordReference = kcs.load(key: STRINGVALUES.kVPN_Pswd)
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
// Check for free subscriber
if self.selectedSever?.serverType == STRINGVALUES.VIP.lowercased() && !Singleton.checkForPaidReciept(){
self.disconnectVPN()
Helper.showAlert(sender: self, title: STRINGVALUES.AppName, message: AlertMessage.kValidateSubscription)
return
}
self.vpnManager.protocolConfiguration = p
self.vpnManager.localizedDescription = STRINGVALUES.AppName
self.vpnManager.isEnabled = true
self.vpnManager.saveToPreferences(completionHandler: self.vpnSaveHandler)
}else{
}
}
}
private var vpnSaveHandler: (Error?) -> Void { return
{ (error:Error?) in
if (error != nil) {
print("Could not save VPN Configurations")
self.removeToast()
return
} else {
do {
try self.vpnManager.connection.startVPNTunnel()
} catch let error {
print("Error starting VPN Connection \(error.localizedDescription)");
self.removeToast()
}
}
}
//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)" )
self.removeToast()
}
}
public func disconnectVPN() ->Void {
vpnManager.connection.stopVPNTunnel()
}
func vpnConnectionStatusChanged(){
let status = self.vpnManager.connection.status
print("VPN connection status = \(status)")
switch status {
case NEVPNStatus.connected:
showToast(msg: STRINGVALUES.kConnected)
case NEVPNStatus.invalid, NEVPNStatus.disconnected :
showToast(msg: STRINGVALUES.kDisconnected)
case NEVPNStatus.connecting , NEVPNStatus.reasserting:
showToast(msg: STRINGVALUES.kConnecting)
case NEVPNStatus.disconnecting:
showToast(msg: STRINGVALUES.kDisconnecting)
default:
print("Unknown VPN connection status")
}
}