Skip to content

Instantly share code, notes, and snippets.

@zqqf16
Last active May 29, 2020 08:11
Show Gist options
  • Save zqqf16/cbcbd2254e6cb965f1a3 to your computer and use it in GitHub Desktop.
Save zqqf16/cbcbd2254e6cb965f1a3 to your computer and use it in GitHub Desktop.
Start IPSec programmatically in iOS 8
- (void)viewDidLoad
{
[super viewDidLoad];
// init VPN manager
self.vpnManager = [NEVPNManager sharedManager];
// load config from perference
[_vpnManager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(@"Load config failed [%@]", error.localizedDescription);
return;
}
NEVPNProtocolIPSec *p = _vpnManager.protocol;
if (p) {
// Protocol exists.
// If you don't want to edit it, just return here.
} else {
// create a new one.
p = [[NEVPNProtocolIPSec alloc] init];
}
// config IPSec protocol
p.username = @"[Your username]";
p.serverAddress = @"[Your server address]";;
// Get password persistent reference from keychain
// If password doesn't exist in keychain, should create it beforehand.
// [self createKeychainValue:@"your_password" forIdentifier:@"VPN_PASSWORD"];
p.passwordReference = [self searchKeychainCopyMatching:@"VPN_PASSWORD"];
// PSK
p.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
// [self createKeychainValue:@"your_psk" forIdentifier:@"PSK"];
p.sharedSecretReference = [self searchKeychainCopyMatching:@"PSK"];
/*
// certificate
p.identityData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];
p.identityDataPassword = @"[Your certificate import password]";
*/
p.localIdentifier = @"[VPN local identifier]";
p.remoteIdentifier = @"[VPN remote identifier]";
p.useExtendedAuthentication = YES;
p.disconnectOnSleep = NO;
_vpnManager.protocol = p;
_vpnManager.localizedDescription = @"IPSec Demo";
[_vpnManager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if (error) {
NSLog(@"Save config failed [%@]", error.localizedDescription);
}
}];
}];
}
- (IBAction)startVPNConnection:(id)sender {
//[[VodManager sharedManager] installVPNProfile];
NSError *startError;
[_vpnManager.connection startVPNTunnelAndReturnError:&startError];
if (startError) {
NSLog(@"Start VPN failed: [%@]", startError.localizedDescription);
}
}
#pragma mark - KeyChain
static NSString * const serviceName = @"im.zorro.ipsec_demo.vpn_config";
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
[searchDictionary setObject:serviceName forKey:(__bridge id)kSecAttrService];
return searchDictionary;
}
- (NSData *)searchKeychainCopyMatching:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
// Add search attributes
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
// Add search return types
// Must be persistent ref !!!!
[searchDictionary setObject:@YES forKey:(__bridge id)kSecReturnPersistentRef];
CFTypeRef result = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &result);
return (__bridge_transfer NSData *)result;
}
- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dictionary);
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:passwordData forKey:(__bridge id)kSecValueData];
status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
if (status == errSecSuccess) {
return YES;
}
return NO;
}
@sdaren
Copy link

sdaren commented Nov 25, 2014

我想问一下,我的VPN连接每次都会提示“VPN 服务器未响应”,是我本地客户端哪里没设置好,还是服务器端需要特别的设置?

@zqqf16
Copy link
Author

zqqf16 commented Nov 28, 2014

一般有以下几种原因:

  • 服务器端没配好,VPN 服务没起来
  • VPN 类型(L2tp/IPSec/IKEv2)不匹配
  • 网络问题

建议先用系统设置中的 VPN 连接,同时抓个包分析一下~

@bronze1man
Copy link

我也遇到过 "VPN 服务器未响应"

原因是局域网内已经有一台机器连接 ipsec ,路由器上有个 ipsec alg
导致只能有一个客户端连接上去,第二个客户端 就 "VPN 服务器未响应"
把路由器上的 ipsec alg 关闭就解决问题了.

@zqqf16
Copy link
Author

zqqf16 commented Jan 20, 2015

@bronze1man IPSec 这样的协议太容易受中间网络设备的影响了

@lhs71173717
Copy link

你好,请教一下,我发现我用5s以上(包括5s)的机器安装含vpn连接功能的app时,安装app的方式是ipa包方式,在安装配置文件时会提示输入共享密钥,但是我直接用xcode安装这个app时,在安装配置文件就不会提示输入共享密钥了,我在我的代码里面是已经填写了共享密钥的,这个是究竟是怎么回事呢,怎么样才能使用ipa包安装应用,且在安装配置文件时不提示输入共享密钥呢??谢谢啦

@zqqf16
Copy link
Author

zqqf16 commented Feb 11, 2015

@lhs71173717 还没遇到过这样的问题……

我做的时候提示输入预共享密钥一般都是因为从 Keychain 中没取到,或者取到的不是“PersistentRef”。

searchDictionary[(__bridge id)kSecReturnPersistentRef] = @YES;

@lhs71173717
Copy link

非常感谢你的回复哦。嗯,我调测了一下,会提示输入共享密钥,都是在keychain取不到值。但是奇怪的是,同一个ipa包,4s、5都可以取到,5s、6、6plus就取不到。。。请教一下,一般会是什么情况导致在keychain中取不到值呢?还有一个问题,我运行了一下你上面的代码提示错误:Save config failed [(null)],不知道是什么原因呢?谢谢啦

@lhs71173717
Copy link

请教一个问题,vpn应用内连接的app在安装完成之后,第一次运行时提示安装描述文件,这个描述文件的签名者显示:未签名,这个要怎么设置才能让描述文件的显示已签名或者显示自己应用的名字呢?是需要添加代码设置,还是需要申请什么证书来设置的呢?

@zqqf16
Copy link
Author

zqqf16 commented Mar 6, 2015

@lhs71173717 我记得用 Network Extension 的方式装完后会显示“已验证”之类的,如果是自己生成的 mobileconfig 文件,可以参考这个Sign Apple mobileconfig files 在 Server 端实现比较简单,客户端的话够呛。

@strong84
Copy link

有办法用L2TP来实现VPN链接吗?我看ios系统VPN设置里可以设置L2TP,有没有办法用代码来实现?

@zqqf16
Copy link
Author

zqqf16 commented Jun 23, 2015

@jameszoh Apple 应该是不打算在 Network Extension 中支持 L2TP 了。

@SoundBlaster
Copy link

Do we have to write
return;
on line 17:
if (_vpnManager.protocol) {
// config exists
}
?

@zqqf16
Copy link
Author

zqqf16 commented Jun 26, 2015

@SoundBlaster
According to my experience (on iOS 8.0), if _vpnManager.protocol exists, assign a new protocol to it will cause an inexplicable error. This may be a bug 😄

I think it would be better to write like this:

NEVPNProtocolIPSec *protocol;

if (_vpnManager.protocol) {
    protocol = _vpnManager.protocol;
} else {
    protocol = [[NEVPNProtocolIPSec alloc] init];
}

// do more configurations...

@sourabhkas
Copy link

Whats localIdentifier & remoteidentifier. Are these values optional?

@zqqf16
Copy link
Author

zqqf16 commented Jun 30, 2015

@sourabhkas
localIdentifier is group name, and remoteidentifier is optional.

@Arvin-J
Copy link

Arvin-J commented Sep 25, 2015

请问一下,从keychain 中获取密码 。没有设置密码,怎么能获取到呢?还是预先在哪里设置加载的密码到keychain里面?

@Yiranfantexi
Copy link

你好,想问下如果用ikev2,和ipsec区别大吗?不需要证书和共享密钥也可以吗?
在代码里需要哪些不一样的设置?
还有就是
p.identityData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"]];
p.identityDataPassword = @"[Your certificate import password]"
这个文件应该是服务器中配置的文件吗?
我曾尝试买的ikev2账号,他给了两个描述文件安装,跟这个有关系吗?
只能拿到苹果的接口文件,很难深入去了解,help,please!

@gnosis520
Copy link

我也出现 VPN 服务器未响应,使用xcode自带的抓包发现没有发出验证请求,而我手动在iphone中配置l2tp连接时是成功的,并且抓包中成功发现VP验证请求,不知道是什么原因引起的

@gnosis520
Copy link

应该说不是没发出验证请求,而是发出的地址为空,也就是肯定没法到达,所以才出现“服务器未响应问题”

@zqqf16
Copy link
Author

zqqf16 commented Nov 13, 2015

@Yiranfantexi 差别不大,具体参见这个 Gist
据我现在掌握的,在 iOS 中,IKEv2 的“设备鉴定”方式只能是证书和共享密钥二选一。
至于那两个描述文件,可以用 Apple Configurator 打开看看,能看见 VPN 的配置信息。

@Yiranfantexi
Copy link

我试过买的账号,当p.authenticationMethod=NEVPNIKEAuthenticationMethodNone ,不要证书和密钥也是可以实现连接的!我也是用strong swan 搭建的服务器安卓的可以实现连接,但是苹果的ikev2不行 只有ipsec 可以!醉了!!

@zqqf16
Copy link
Author

zqqf16 commented Nov 23, 2015

@Yiranfantexi IPSec IKE(v1/v2) 各个公司的实现方式都不一样,支持的加密算法什么的也是乱七八糟 😂

@Cutezhao
Copy link

你好,我使用共享密钥来的方式连接,再ios9的时候没有问题,但是ios8再安装描述文件的时候也出现了上边的描述文件未签名,还会出现闪退,有什么解决的办法吗

@Cutezhao
Copy link

@lhs71173717你好我问一下 你那个ios8 描述文件的问题有解决办法吗 ?

@weqeo
Copy link

weqeo commented Mar 16, 2016

@Cutezhao,你好,请问你的描述文件会不会提示 Save config faild[(null),我的会提示,请问你现在解决了吗?

@al-ht10
Copy link

al-ht10 commented Mar 21, 2016

How to use the above demo if .ovpn file is provided?

@duoduoyi
Copy link

点击按钮的时候 提示“Start VPN failed: [The operation couldn’t be completed. (NEVPNErrorDomain error 1.)]” 这怎么回事啊

Copy link

ghost commented Jun 6, 2016

Just call loadFromPreferencesWithCompletionHandler right before startVPNTunnelAndReturnError.
It should helps you. @duoduoyi

@vetrek
Copy link

vetrek commented Jul 3, 2016

Out of curiosity, is it possible to ignore local and remote identifier? or are they both mandatory?

@zhangyang91
Copy link

请问想要开发VPN的话是必须要申请开发权限吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment