-
-
Save IhorYachmenov/fb63fd19f30541780950b6670e8a2865 to your computer and use it in GitHub Desktop.
import Foundation | |
import NetworkExtension | |
class VPNIKEv2Setup { | |
static let shared = VPNIKEv2Setup() | |
let vpnManager = NEVPNManager.shared() | |
func initVPNTunnelProviderManager() { | |
print("CALL LOAD TO PREFERENCES...") | |
self.vpnManager.loadFromPreferences { [self] (error) -> Void in | |
if((error) != nil) { | |
print("VPN Preferences error: 1 - \(String(describing: error))") | |
} else { | |
let IKEv2Protocol = NEVPNProtocolIKEv2() | |
IKEv2Protocol.authenticationMethod = .certificate | |
IKEv2Protocol.serverAddress = VPNServerSettings.shared.vpnServerAddress | |
IKEv2Protocol.remoteIdentifier = VPNServerSettings.shared.vpnRemoteIdentifier | |
IKEv2Protocol.localIdentifier = VPNServerSettings.shared.vpnLocalIdentifier | |
IKEv2Protocol.useExtendedAuthentication = false | |
IKEv2Protocol.ikeSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES256GCM | |
IKEv2Protocol.ikeSecurityAssociationParameters.diffieHellmanGroup = .group20 | |
IKEv2Protocol.ikeSecurityAssociationParameters.integrityAlgorithm = .SHA512 | |
IKEv2Protocol.ikeSecurityAssociationParameters.lifetimeMinutes = 1440 | |
IKEv2Protocol.childSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES256GCM | |
IKEv2Protocol.childSecurityAssociationParameters.diffieHellmanGroup = .group20 | |
IKEv2Protocol.childSecurityAssociationParameters.integrityAlgorithm = .SHA512 | |
IKEv2Protocol.childSecurityAssociationParameters.lifetimeMinutes = 1440 | |
IKEv2Protocol.deadPeerDetectionRate = .medium | |
IKEv2Protocol.disableRedirect = true | |
IKEv2Protocol.disableMOBIKE = false | |
IKEv2Protocol.enableRevocationCheck = false | |
IKEv2Protocol.enablePFS = true | |
IKEv2Protocol.useConfigurationAttributeInternalIPSubnet = false | |
IKEv2Protocol.serverCertificateIssuerCommonName = VPNServerSettings.shared.vpnServerCertificateIssuerCommonName | |
IKEv2Protocol.disconnectOnSleep = false | |
IKEv2Protocol.certificateType = .ECDSA384 | |
IKEv2Protocol.identityDataPassword = VPNServerSettings.shared.p12Password | |
IKEv2Protocol.identityData = self.dataFromFile() | |
self.vpnManager.protocolConfiguration = IKEv2Protocol | |
self.vpnManager.localizedDescription = "Venom VPN" | |
self.vpnManager.isEnabled = true | |
self.vpnManager.isOnDemandEnabled = false | |
//Set rules | |
var rules = [NEOnDemandRule]() | |
let rule = NEOnDemandRuleConnect() | |
rule.interfaceTypeMatch = .any | |
rules.append(rule) | |
// self.vpnManager.onDemandRules = rules | |
print("SAVE TO PREFERENCES...") | |
//SAVE TO PREFERENCES... | |
self.vpnManager.saveToPreferences(completionHandler: { (error) -> Void in | |
if((error) != nil) { | |
print("VPN Preferences error: 2 - \(String(describing: error))") | |
} 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 - \(String(describing: error))") | |
} 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 - \(String(describing: error))") | |
//Show alert here | |
print("title: Oops.., message: Something went wrong while connecting to the VPN. Please try again.") | |
print(startError.debugDescription) | |
} else { | |
print("Starting VPN...") | |
} | |
} | |
}) | |
} | |
}) | |
} | |
} | |
} | |
//MARK:- Connect VPN | |
static func connectVPN() { | |
VPNIKEv2Setup().initVPNTunnelProviderManager() | |
} | |
//MARK:- Disconnect VPN | |
static func disconnectVPN() { | |
VPNIKEv2Setup().vpnManager.connection.stopVPNTunnel() | |
} | |
//MARK:- Disconnect VPN | |
static func testConnect() { | |
do { | |
try VPNIKEv2Setup().vpnManager.connection.startVPNTunnel() | |
} catch let error { | |
print(error) | |
} | |
} | |
//MARK:- check connection staatus | |
static func checkStatus() { | |
let status = VPNIKEv2Setup().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") | |
} | |
} | |
func dataFromFile() -> Data? { | |
let rootCertPath = Bundle.main.url(forResource: "phone", withExtension: "p12") | |
print(rootCertPath?.absoluteURL as Any) | |
return try? Data(contentsOf: rootCertPath!.absoluteURL) | |
} | |
} | |
class VPNServerSettings: NSObject { | |
static let shared = VPNServerSettings() | |
let p12Password = "*****" // password from file certificate "****.p12" | |
let vpnServerAddress = "******" | |
let vpnRemoteIdentifier = "*******" // In my case same like vpn server address | |
let vpnLocalIdentifier = "phone@caf1e9*******.algo" | |
let vpnServerCertificateIssuerCommonName = "*******" // In my case same like vpn server address | |
} | |
@Dearimranullah It's possible this code worked well in 2019 not sure about it right now, in your case I guess the problem with "permission denied" this functionality requires VPN permission from the user, if you forgot about that this code wouldn't work absolutely, also, your App Store account should have a Company license if your account has another type of license you will not pass the App Store Review Process, rule number 5.4 "Apps offering VPN services must utilize the NEVPNManager API and may only be offered by developers enrolled as an organization."
@Dearimranullah select your Target than tab "Signing & Capabilities" and you should enable capability "Network Extension -> and check Packet Tunnel" and add another one capability "Personal VPN", and in your entitlements file add "Outgoing Network Connections" - > YES, maybe last step will added automatically can't recall that. Then when you will call loadFromPreferences you should see alert with permission access
How do I use it in content view? Can you give me an example?
@Rayan25062011 Hi, in this realization VPNIKEv2Setup is Singelton because you can call any non-static function such as VPNIKEv2Setup.initVPNTunnelProviderManager.initVPNTunnelProviderManager(), and after that in any comfortable place use VPNIKEv2Setup. connectVPN() and disconnectVPN() etc., or rewrite realization for your requirements
@Rayan25062011 Hi, in this realization VPNIKEv2Setup is Singelton because you can call any non-static function such as VPNIKEv2Setup.initVPNTunnelProviderManager.initVPNTunnelProviderManager(), and after that in any comfortable place use VPNIKEv2Setup. connectVPN() and disconnectVPN() etc., or rewrite realization for your requirements
Thank you very much @IhorYachmenov
not working for me it give this error
CALL LOAD TO PREFERENCES...
2023-11-06 20:14:16.584739+0500 MagicChatApp[1058:203441] [] Failed to load configurations: Error Domain=NEConfigurationErrorDomain Code=10 "permission denied" UserInfo={NSLocalizedDescription=permission denied}
2023-11-06 20:14:16.891598+0500 MagicChatApp[1058:203172] [] Failed to load the configuration: Error Domain=NEVPNErrorDomain Code=5 "permission denied" UserInfo={NSLocalizedDescription=permission denied}
VPN Preferences error: 1 - Optional(Error Domain=NEVPNErrorDomain Code=5 "permission denied" UserInfo={NSLocalizedDescription=permission denied})