Can I set up IKEv2 with NEVPNManager using mobileconfig data?

We are trying to load/install an IPSEC VPN through our app, and have not yet been successful. According to Eskimo (see https://forums.developer.apple.com/thread/70696), the preferred mechanism would be NEVPNManager, with other options being hosting on an external web server, or as a last resort a web server in the app.


We have a mobileconfig file from our VPN server that works if loaded manually, but have been unsuccessful using it with other mechanisms.


Web Server:

We do not have an external web server that would be a good place to host the mobileconfig files right now. However, we do have access to a cloud server data storage from which we can pull the mobileconfig file. We do use GCDWebServer in the app and spent some time trying to get that to work (ingesting and saving a mobileconfig, and then opening the URL), but we were unsuccessful in the short time that we tried. In reality, we’d rather not use that mechanism anyway because we don’t want to end up in a browser window outside of the app.


NEVPNManager and Personal VPN

We have ingested and parsed the mobileconfig file (from cloud server), setting values in NEVPNManager and NEVPNProtocolIKEv2 protocol object. But when we try to start the VPN, it is unsuccessful. Looking at console messages, it looks like the issue is related to the root CA. Some of the relevant console messages we are seeing are as follows:


BACKTRACE failed to retrieve remote CA cert data by CN (***.***.***.***)

remoteCertAuthorityArray missing from config

Certificate authentication data could not be verified

Failed to process IKE Auth packet (connect)


Originally the VPN server was using a mobileconfig with a self-signed root cert, but then after reading Eskimo’s responses (in https://forums.developer.apple.com/thread/84679 and https://forums.developer.apple.com/thread/114575 and https://forums.developer.apple.com/thread/108004 and https://forums.developer.apple.com/thread/76227), we were able use Let’s Encrypt to issue the certificate used in the mobileconfig. And yet we still are getting the exact same result, and same messages in Console.


A few additional details:

I read in the mobileconfig file as a dictionary and pulled the cert information from PayloadContent in its section in the mobileconfig. The mobileconfig has Base64 encoded pkcs12 certificate with a password (PayloadDescription for that section says "Adds a PKCS#12-formatted certificate”). The PayloadContent is a “data” type tag, so when I get it’s value it looks like it correctly reads it into the NSData object, and so I set the following on the NEVPNProtocolIKEv2 protocol object. Does this look correct?


protocol.identityDataPassword = [certDictionary valueForKey:@"Password"];

protocol.identityData = [certDictionary valueForKey:@"PayloadContent"];



There is also a 2nd cert section, with PayloadDescription of "Adds a CA root certificate”. There doesn’t seem to be a place for this in NEVPNManager, but I thought maybe with a valid cert, it would get that information from the VPN server?


if I run something like this against the mobileconfig file:

openssl pkcs7 -inform DER -print_certs -in test.mobileconfig > test.pem
openssl x509 -in test.pem -noout -text


It shows (along with all the other info):


Issuer: O=Digital Signature Trust Co., CN=DST Root CA X3

Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3


And yet the numerous other things I’ve tried, end up showing: Issuer: CN=***.***.***.*** (VPN server IP, which is also set as serverCertificateIssuerCommonName in the mobileconfig)


In spite of spending quite a bit of time looking at this, I’m wondering if I’m just still ignorant about how certs work.


Has anyone tried to parse the cert data out of a mobileconfig file to set up a VPN connection via NEVPNManager?

Replies

Originally the VPN server was using a mobileconfig … we were able use Let’s Encrypt to issue the certificate used in the mobileconfig.

I don’t understand this. The certificate for your VPN server doesn’t need to be in the configuration profile, it needs to be returned by the VPN server as part of the VPN handshake.

So, to clarify, how many payloads are in your configuration profile? And what are their payload types?

Share and Enjoy

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

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

Thank you for the response and sorry for the confusion. There are two payloads in the configuration file. One PayloadType is "com.apple.security.pkcs12" and one is "com.apple.security.root".


We are using an AlgoVPN server that is automatically setup with a set of scripts. If I go to manually install the mobileconfig it produces with the default setup, in the details of Settings -> Profile Downloaded, it shows two certificates and shows up as "Not Signed". It looks like the root CN is just the IP address of the server.


So, we updated the server to use Let's Encrypt (or at least we tried). With the updated profile, if I manually load and then look in Settings, it shows as signed and verified. In More Details it shows two certificates (similar to the default with one of them having the IP address of the server as the CN) as well as two signing certificates (related to Let's Encrypt).


In https://forums.developer.apple.com/thread/84679, you mentioned that "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." Our profile does have that payload type. But then you also said "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." That's what we were trying to accomplish with the updated profile. Are we still missing or misunderstanding something?

So, we updated the server to use Let's Encrypt (or at least we tried).

At this point I’d recommend that you confirm that. I’ve yet to find a way to get the certificate list from an IPsec or IKEv2 VPN server (Anyone know of an equivalent to

openssl s_client
for VPN?), so my advice is that you edit your configuration profile to remove the
com.apple.security.root
payload. If that works, you should be able to get Personal VPN working.

You can edit the profile by hand or use Apple Configurator.

Share and Enjoy

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

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

When you say "I’d recommend that you confirm that", how would I do that? If I run the following against the profile it shows information about Let's Encrypt, but maybe this isn't what you mean?


openssl pkcs7 -inform DER -print_certs -in test.mobileconfig > test.pem

openssl x509 -in test.pem -noout -text


I can remove the

com.apple.security.root
payload from the mobileconfig by hand, but I'm not sure how that helps me with NEVPNManager? I'm not actually using that section for anything in the NEVPNManager or IKEv2 parameters in code because I'm not sure where it fits. My lack of understand of certificates and chains could be causing issues here. Is there something in the pkcs12 that points to that invalid root?


Edit: After looking at this more and talking to my co-worker about how the profile was created, I think we are not using Let's Encrypt the way that I originally thought. It seems Let's Encrypt basically just signed the profile that was created by AlgoVPN (after the fact). I think we still need to figure out how to create the profile with a valid certificate, which may be difficult with Algo since everything is automated and done by scripts. I think they use self-signed certs because it was designed to be a "personal" VPN. From a response to an issue on their github "They're self-signed...and the only person with the keys is you. This is the whole point of Algo -- you're the only one who gets to see your traffic."


I'll try removing that root payload section, but if it helps, here's the current sample code I'm using with some things hardcoded (to values that are in the profile) and some pulled from the mobileconfig:


Side note: Along the way I tried a bunch of things from various posts that may or may not not be useful. For instance, I "loadFromPreferencesWithCompletionHandler" and then "save" and then "load" again before trying to start the VPN because of comments on various forums.


NSDictionary *ipdict = [NSDictionary dictionaryWithContentsOfURL:ipsecProfile];

NEVPNManager *manager = [NEVPNManager sharedManager];


manager.onDemandEnabled = YES;

NSMutableArray *rules = [[NSMutableArray alloc] init];

NEOnDemandRuleConnect *connectRule = [NEOnDemandRuleConnect new];

NEOnDemandRuleDisconnect *disconnectRule = [NEOnDemandRuleDisconnect new];

[rules addObject:connectRule];

[rules addObject:disconnectRule];


NEVPNProtocolIKEv2 *protocol = [[NEVPNProtocolIKEv2 alloc] init];

protocol.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;


protocol.childSecurityAssociationParameters.diffieHellmanGroup = NEVPNIKEv2DiffieHellmanGroup20;

protocol.childSecurityAssociationParameters.encryptionAlgorithm = NEVPNIKEv2EncryptionAlgorithmAES256GCM;

protocol.childSecurityAssociationParameters.integrityAlgorithm = NEVPNIKEv2IntegrityAlgorithmSHA512;

protocol.childSecurityAssociationParameters.lifetimeMinutes = 1440;


protocol.deadPeerDetectionRate = NEVPNIKEv2DeadPeerDetectionRateMedium;

protocol.disableMOBIKE = NO;

protocol.disableRedirect = YES;

protocol.enableRevocationCheck = NO;

protocol.enablePFS = YES;


protocol.IKESecurityAssociationParameters.diffieHellmanGroup = NEVPNIKEv2DiffieHellmanGroup20;

protocol.IKESecurityAssociationParameters.encryptionAlgorithm = NEVPNIKEv2EncryptionAlgorithmAES256GCM;

protocol.IKESecurityAssociationParameters.integrityAlgorithm = NEVPNIKEv2IntegrityAlgorithmSHA512;

protocol.IKESecurityAssociationParameters.lifetimeMinutes = 1440;


NSArray *parray = [ipdict objectForKey:@"PayloadContent"];

NSDictionary *pcontent = parray[0];

NSDictionary *pcontent2 = parray[1];

protocol.localIdentifier = [pcontent valueForKeyPath:@"IKEv2.LocalIdentifier"];

protocol.certificateType = NEVPNIKEv2CertificateTypeECDSA384;

protocol.serverCertificateIssuerCommonName = [pcontent valueForKeyPath:@"IKEv2.ServerCertificateIssuerCommonName"];

protocol.serverAddress = [pcontent valueForKeyPath:@"IKEv2.RemoteAddress"];

protocol.remoteIdentifier = [pcontent valueForKeyPath:@"IKEv2.RemoteIdentifier"];

protocol.useConfigurationAttributeInternalIPSubnet = NO;


protocol.proxySettings.HTTPEnabled = NO;

protocol.proxySettings.HTTPSEnabled = NO;

protocol.username = [pcontent valueForKeyPath:@"UserDefinedName"];


protocol.identityDataPassword = [pcontent2 valueForKey:@"Password"];

protocol.identityData = [pcontent2 valueForKey:@"PayloadContent"];


protocol.useExtendedAuthentication = NO;

protocol.disconnectOnSleep = NO;


[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {

if(error) {

DDLogInfo(@"IPSEC loadFromPreferencesWithCompletionHandler error: %@", error.localizedDescription);

return;

}

manager.onDemandEnabled = YES;

[manager setOnDemandRules:rules];

manager.protocolConfiguration = protocol;

[manager setLocalizedDescription:@"IPSEC VPN"];

[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {

if(error) {

DDLogInfo(@"IPSEC saveToPreferencesWithCompletionHandler error: %@", error.localizedDescription);

return;

}


[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {

if(error) {

DDLogInfo(@"IPSEC loadFromPreferencesWithCompletionHandler2 error: %@", error.localizedDescription);

return;

}

NSError *startError;

[manager.connection startVPNTunnelAndReturnError:&startError];

if(startError) {

DDLogInfo(@"IPSEC Start error: %@", startError.localizedDescription);

}

}];

}];

}];

When you say "I’d recommend that you confirm that", how would I do that?

  1. Remove the

    com.apple.security.root
    payload from your configuration profile.
  2. Install it.

  3. Does it work?

If it doesn’t work, you won’t be able to get Personal VPN to work because

NEVPNManager
offers a strict subset of what you can do in a configuration profile. If it does work, you can probably get Personal VPN to work, it’s just a question of using it in exactly the right way (it is, in my experience, a rather persnickety API).

Share and Enjoy

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

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

Ok, I tried removing the root payload, installed it, and it would not connect 😟. Still working on how to get a mobileconfig that we can parse and and use the certificate data with NEVPNManager. Thanks!

I tried removing the root payload, installed it, and it would not connect

Right, so your IKEv2 server is not use a certificate that’s trusted by the system. Such servers are not supported by Personal VPN.

Share and Enjoy

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

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