cannot establish Ikev2 connection programmatically

Hello. I am trying to setup Virtual Private Network (VPN) based on IKEV2 protocol which communicate with a Libreswan backend server. When I create .mobileconfig using AppleConfigurator2 all works fine.

I checked out the documentation and found that "On iOS for

NEVPNProtocolIPSec
and
NEVPNProtocolIKEv2
objects the identity should be set using the
identityData
property. And identityData is the "The certificate and private key components of the tunneling protocol authentication credential, encoded in

PKCS12 format.".

Here is my function to establish VPN Connection:

func setupVPNConnectio(withData pkcs12Data: Data)
{
let manager: NEVPNManager = NEVPNManager.shared()
manager.loadFromPreferences(completionHandler: { error in
if let e = error {
print("error: " + e.localizedDescription)
return
}
let protocolIKEv2 = NEVPNProtocolIKEv2()
protocolIKEv2.authenticationMethod = .certificate
protocolIKEv2.certificateType = .RSA
protocolIKEv2.serverAddress = "serverAddress"
protocolIKEv2.remoteIdentifier = "remoteId"
protocolIKEv2.localIdentifier = "localId"
protocolIKEv2.identityData = pkcs12Data
protocolIKEv2.identityDataPassword = "password"
protocolIKEv2.deadPeerDetectionRate = .medium
protocolIKEv2.ikeSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES128
protocolIKEv2.ikeSecurityAssociationParameters.integrityAlgorithm = .SHA256
protocolIKEv2.ikeSecurityAssociationParameters.diffieHellmanGroup = .group14
protocolIKEv2.ikeSecurityAssociationParameters.lifetimeMinutes = 1440
protocolIKEv2.childSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES128
protocolIKEv2.childSecurityAssociationParameters.integrityAlgorithm = .SHA256
protocolIKEv2.childSecurityAssociationParameters.diffieHellmanGroup = .group14
protocolIKEv2.childSecurityAssociationParameters.lifetimeMinutes = 1440
manager.protocolConfiguration = protocolIKEv2
manager.isEnabled = true
manager.saveToPreferences(completionHandler: { error in
guard error == nil else {
print("NEVPNManager.saveToPreferencesWithCompletionsHandler failed: \(error!.localizedDescription)")
return
}
do {
try manager.connection.startVPNTunnel(options: nil)
} catch (let exception) {
print(exception)
}
})
})
}



But when I am trying to connect nothing is happened (without any errors). I connected VPN loging profile and AppleConfigurator2 to check logs. Here is output from console.


pfkey received SA is NULL

Certificate evaluation error =

kSecTrustResultRecoverableTrustFailure

Certificate is not trusted

Certificate authentication data could not be verified

Failed to process IKE Auth packet


When I trying to convert pkcs12 data to SecIdentity and evaluate SecTrust - nothing changed.


func evaluateIdentity(pkcs12Data: Data, password: String) throws
{
var importResult: CFArray? = nil
let err = SecPKCS12Import(pkcs12Data as NSData, [kSecImportExportPassphrase as String: password] as NSDictionary, &importResult)
guard err == errSecSuccess else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
}
let identityDictionaries = importResult as! [[String:Any]]
let certificates = identityDictionaries[0] [kSecImportItemCertChain as String] as! [SecCertificate]
let identity = identityDictionaries[0][kSecImportItemIdentity as String] as! SecIdentity
let trust = identityDictionaries[0][kSecImportItemTrust as String] as! SecTrust
var status = SecTrustSetAnchorCertificates(trust, certificates as CFArray )
var resultType = SecTrustResultType.invalid
print(status)
status = SecTrustEvaluate(trust, &resultType)
//Received status 4
}


I also tried to save identiy to the keychain, but again - nothing changed.


func saveIdentity(identity: SecIdentity) {
var certificateRef: SecCertificate? = nil
SecIdentityCopyCertificate(identity, &certificateRef)
let subject = SecCertificateCopySubjectSummary(certificateRef!)
let saveQuery = [
kSecClass as String : kSecClassIdentity,
kSecValueRef as String : identity,
kSecReturnRef as String : kCFBooleanTrue,
kSecAttrAccessible as String : kSecAttrAccessibleAlways,
kSecAttrLabel as String : subject!
] as [String : Any]
var item: CFTypeRef? = nil
var status = SecItemAdd(saveQuery as CFDictionary, &item)
// Received status 0
}



What I am doing wrong? And is it possible to establish this type of VPN connection programmatically?


Thanks.

Answered by DTS Engineer in 252676022

Your profile confirmed what I suspected based on the following snippet from your original post:

Certificate evaluation error = kSecTrustResultRecoverableTrustFailure Certificate is not trusted

The profile contains a custom root certificate (a payload of type

com.apple.security.root
). Presumably your VPN server has a certificate issued by that custom root certificate. That works when you install the profile (your custom root certificate becomes trusted by the system as a whole) but is problematic for NEVPNManager because:
  • iOS has no API to install a trusted root certificate globally

  • NEVPNManager has no way to override IKEv2 server trust evaluation such that it’ll trust your custom root certificate in this context

If you want to continue down the NEVPNManager path you will have to get a trusted CA to issue you a certificate for your VPN server.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

When I create .mobileconfig using AppleConfigurator2 all works fine.

Can you post that configuration profile? Feel free to elide anything sensitive. Alternatively, if you want to keep the whole thing private you could email it to me (my address is in my signature, below).

IMPORTANT I’m interested in the whole profile, not just the VPN payload part of it.

When I trying to convert pkcs12 data to SecIdentity and evaluate SecTrust - nothing changed.

That’s not unexpected. On modern systems

identityReference
and
identityData
with
identityDataPassword
are completely equivalent. I generally use the latter because it’s easier.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Already done. Check your email. Thanks.

Accepted Answer

Your profile confirmed what I suspected based on the following snippet from your original post:

Certificate evaluation error = kSecTrustResultRecoverableTrustFailure Certificate is not trusted

The profile contains a custom root certificate (a payload of type

com.apple.security.root
). Presumably your VPN server has a certificate issued by that custom root certificate. That works when you install the profile (your custom root certificate becomes trusted by the system as a whole) but is problematic for NEVPNManager because:
  • iOS has no API to install a trusted root certificate globally

  • NEVPNManager has no way to override IKEv2 server trust evaluation such that it’ll trust your custom root certificate in this context

If you want to continue down the NEVPNManager path you will have to get a trusted CA to issue you a certificate for your VPN server.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Okay, thank you very much. I will ask a customer for trusted CA.


But

"If you want to continue down the NEVPNManager path..." Can I choose another path and handle this situation on myself?

Can I choose another path …?

The other path I was referring to was setting up the VPN via a configuration profile. That makes sense in some circumstances (for example, on MDM managed devices, wherein it’s common for the enterprise to install its root certificate for lots of different reasons, not just VPN).

I guess you could also do a custom VPN transport (NEPacketTunnelProvider) but I wouldn’t recommend that because it’s a lot more work.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
cannot establish Ikev2 connection programmatically
 
 
Q