iOS开发使用NetworkExtension框架编写VPN应用

作者: 槛外人_ | 来源:发表于2017-05-02 16:25 被阅读9868次

最近闲来无聊,想自己做个ios平台代理上网的应用,最直接的方式就是通过VPN了,刚好iOS对VPN有很好的支持。于是花点时间查了下iOS的官方开发文档,顺便学习一下这种相关框架(NetworkExtension)的使用,也算是对知识点的查漏补缺了。


苹果是从iOS 8开始,才开放了新的框架 NetworkExtension ,该框架提供了配置和控制VPN支持和wifi热点相关的接口。通过该框架,我们可以很方便的实现VPN以及wifi热点相应的功能。接下来,我将介绍如何使用该框架实现一个简单的VPN功能。
在平常的iOS开发当中,VPN功能作为Capabilities中的一项,需要我们手动去工程的Capabilities中配置且打开。如下图所示:

4900F6B8-89BD-4CD9-9406-09CCB2C596CE.png
注:<small>相关的AppID以及开发证书(或者发布证书)需要在苹果开发者中心里面开启该项功能,即在相应的App Id里面 edit id ---> 确保Personal VPN前面的复选框被选中!</small>
屏幕快照 2017-05-02 下午2.52.12.png

下面开始代码实现,关于VPN的种类以及不同种类之间的差异和优缺点,大家请自行谷歌百度之,这里不再作过多的赘述。需要注意的是早在iOS10,苹果就去掉了PPTP的vpn连接方式(而MacOs是在EiCapitain出来之后去掉的)。以下,我仅介绍其中一种VPN连接方式---IKEV2[1]

首先引入相关框架:

@import NetworkExtension

打开应用需要检测之前安装了VPN描述文件(profile):

NEVPNManager *manager = [NEVPNManager sharedManager];
    [manager loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
        if(error) {
            NSLog(@"Load error: %@", error);
        } else {
            // No errors! The rest of your codes goes here..
            NEVPNProtocolIKEv2* ikev = [[NEVPNProtocolIKEv2 alloc] init];
            ikev.certificateType = NEVPNIKEv2CertificateTypeRSA;
            ikev.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
            ikev.useExtendedAuthentication = YES;
            
            ikev.username = _wzUsername;
            ikev.passwordReference = 使用iOS钥匙串(keyChain)存入的密码;
            ikev.serverAddress = @"**. **. **. **";  
            ikev.remoteIdentifier = @"***";
            ikev.identityReference =  使用iOS钥匙串(keyChain)存入的identity;
            
            [manager setEnabled:YES];
            [manager setProtocol:ikev];
            [manager setOnDemandEnabled:NO];
            [manager setLocalizedDescription:@"MyVPN"];
            
            [manager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
                if(error) {
                    NSLog(@"Save error: %@", error);
                }
                else {
                    NSLog(@"Saved!");
                }
            }];
        }
    }];

NEVPNManager是个VPN的管理单例,在整个VPN的操作中会被频繁使用。首先调用loadFromPrefreferencesWithCompletionHandler函数加载相关配置,如果没有发生错误就使用类NEVPNProtocolIKEV2配置VPN的类型(该例子使用的是IKEV2类型,类似的还可以通过NEVPNProtocolIPSec设置IPSec类型的VPN,配置方式大同小异),关键点在于NEVPNProtocolIKEv2类的各个配置参数:

  • 如果authenticationMethod字段采用的是NEVPNIKEAuthenticationMethodCertificate,那么在使用该VPN之前,我们必须引导用户在ios设备安装从我们的服务器导出的证书。
  • passwordReference 字段必须使用从KeyChain里面取出的密码,在此之前,我们可以先将VPN的密码提前使用ios的keyChain进行持久化。
  • identityReference 则是使用keyChain存储的VPN证书。
  • username VPN的账号
  • serverAddress VPN的服务器地址,可以是域名或者IP地址
  • remoteIdentifier 远程服务器的ID,该参数可以在自己服务器的VPN配置文件查询得到
    如果用户尚未安装描述文件,则调用saveToPreferencesWithCompletionHandler会弹出系统提示,提醒用户安装相应的描述文件。

以上任何一个字段填写错误都会导致VPN连接不成功。一切就绪后,使用如下方式发起VPN的连接请求:

NSError *startError;
[[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];

一般情况在开始连接之前最好先加载VPN配置,如果没有错误再进行连接,(如果连接成功后,设备顶部状态栏会出现一个VPN图标。)操作如下:

[[NEVPNManager sharedManager] loadFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
        if(error)
        {
            NSLog(@"VPN load error->%@", error);
        }
        else
        {
            NSError *startError;
            [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
            
            if(startError) {
                NSLog(@"Start error: %@", startError.localizedDescription);
            } else {
                NSLog(@"Connection established!");
            }
        }
    }];

综上,通过苹果的框架去实现一个简单的VPN功能还是相当容易的,如果各位在使用上遇到了什么问题,请留言!。
最后,厚着脸皮安利一下自己最近写的一个APP(水平有限,有需要改进的地方还望高手指出),希望大家多多支持,多多交流,共同进步😂😂
https://itunes.apple.com/us/app/bigvpn/id1225850922?mt=8


  1. IKEv2连接在用户自身网络状况经常变化的情况下仍旧能够维持加密连接,而不会出现频繁闪断、断开又重连之类的情况,能大大提高网络连接的稳定性。IKEv2能够拥有比PPTP/L2TP更加高效的网络通讯效率。IKEv2是IKE(v1)的改良版,使用了公钥证书和密码等多重认证,弥补了v1时代的安全性上的不足。比PPTP/L2TP更是可靠许多。同时IKEv2还支持硬件加速,保证了高效的传输效率。(引用自网络)

相关文章

网友评论

  • bd5be3c963bf:你好,能加个微信吗,想了解一些问题。
  • 2bafb5886bb2:请问 有上线的时候因为非公开API 被拒的吗?
  • LEOHAI:博主 您好 我使用NEVPNIKEAuthenticationMethodSharedSecret 进行认证的 密码 用户名都设置好了 但是每次连接的时候 都需要手动输入密码 是怎么回事呢 ?
  • 成热了:你好大神 我这个用的IPSEC做的 遇到这种错误
    018-06-04 13:21:35.746270+0800 EFBMuefb[906:183449] Start error: 未能完成操作。(“NEVPNErrorDomain”错误 1。)
    您知道是什么问题么
  • 开发界的白小白:大佬,请问一下,共享秘钥是什么//共享秘钥可以和密码同一个. [self createKeychainValue:Password forIdentifier:@"PSK"]; p.sharedSecretReference = [self searchKeychainCopyMatching:@"PSK"]; PSK这个参数是什么,该怎么填
  • miwer:你好 你知道怎么指定应用走VPN吗
    栋柠柒:@帆ny 我想他说的意思是只拦截指定的url吧
    帆ny:兄弟,你这个问题有思路吗?怎么指定应用走VPN?
  • 绪雨成澜:楼主你好,现在我的服务器端只给我提供了VPN的服务器地址、IP和密码三个东西,我在AppStore上下载了一个名为FirstWingy的APP,在上面填写了上述三个东西之后就可以连上VPN了,但是我使用你给的流程方法编写了程序,有些字段后端也不知道给我提供什么,比如说remoteIdentifier、username等,后端人员就不知道这些是什么东西。我们后端采用的是shadowSockets。
    时光浅影:@绪雨成澜 我也想问这个问题,请问有解决办法吗?
  • 代码移动工程师:请问你用的是什么服务器
  • 代码移动工程师:remoteIdentifier 远程服务器的ID 这个在哪里可以查到呢,我用的搬瓦工的服务器
    代码移动工程师:@槛外人_ 我用的搬瓦工的服务器里面只有openVpn ,好像弄不成
    槛外人_:这个是vpn服务器给你的参数
  • 凡克斯:你好,楼主,我用的ipsec方式连接的,但是总是提示我未提供共享秘钥,这是怎么回事呢
    成热了:你好 请问您这经历过 这个错误么 我用的也是IPSEC
    018-06-04 13:21:35.746270+0800 EFBMuefb[906:183449] Start error: 未能完成操作。(“NEVPNErrorDomain”错误 1。)
  • 改鞋归正:楼主这个App 是不是只用到了NetworkExtension 这个框架
  • 我本善良:下载了app,开启vpn之后,上不去网?需要注册账号吗?
  • 风寻月觅:想问下楼主搭建的服务器,建的是ss?
  • 96b92fbcc53c:你好,请问VPN账号在哪里买的?方便给我说下嘛?最近想练习下。谢谢
  • David_这样很好:NetworkExtension 使用这个框架应该是需要向苹果申请权限的吧?
    槛外人_:@David_这样很好 不用的,只需要打开相应的权限即可
  • 苜蓿鬼仙::sob: 延迟很高
  • ProgramDouglas:你好,我想请问证书相关的问题。

    在上文写到:
    如果authenticationMethod字段采用的是NEVPNIKEAuthenticationMethodCertificate,那么在使用该VPN之前,我们必须引导用户在ios设备安装从我们的服务器导出的证书。
    那么,该证书是怎么安装的?是在创建这个vpn配置的时候,会引导我们去下载这个描述文件吗?还是说得提前通过其他方式下载好这个描述文件呢?

    另外,如果我想通过p12文件和密码的方式,是否可行呢?
    谢谢!
    槛外人_:@道格拉斯hahaha 我两种方式都使用过,你首先要确定你要用的是哪种? 如果是第一种必须让用户信任你们服务器的证书,否则连接会失败的。第二种的话务必设置好相应的密码,提前把p12证书放在bundle里面。
    ProgramDouglas:@槛外人_ 首先感谢回复。1.的方式,我明白的。因为我们目前测试的时候,就是先使用邮件客户端去安装这个证书。安装好了。才能使用。 2.的方式,不知道你是否使用过P12的方式?identityData和identityDataPassword 都正确设置好了,但是还是拨不上。怀疑是 非信任证书的问题。你们使用的证书是 信任过的吗?

    最后,我下载了您发布的VPN。体验上应该是2.的方式吧?所以求解答,不胜感激。
    槛外人_:你好, 有两种方式:1,如果使用证书的话,你必须在用户连接之前引导其安装证书,你可以把该证书放在你的服务器上,该证书会有个外链,之后引导用户使用浏览器打开该外链即可自动安装。2,如果是使用P12文件的证书话,因为P12是有密码的,所以需要设置identityDataPassword参数,该参数即为P12的密码。文件名跟上文一样设置即可。
  • 小宗师cz:江湖告急,authenticationMethod = NEVPNIKEAuthenticationMethodCertificate; 设置证书认证,这个证书需要怎么配置到程序中
    槛外人_:@小宗师cz 嗯, 是这样的。如果你希望以免证书或者PSK方式连接VPN,那么您必须在服务器作相应的配置。
    小宗师cz:@槛外人_ 很感谢回复!现在我链接需要安装服务器给我的证书文件、我看你应用实现面安装证书的步骤、免安装证书,需要服务端做是这样吗?感谢
    槛外人_:请您按照上文配置即可
  • c3b3f1ad6e39:怎么给ios9配置pptp类型的vpn
  • EdwardSelf:你好,请问VPN的地址,账号需要自己去买然后填进去吗?
    EdwardSelf:@槛外人_ 我还想问问,关于remoteIdentifier和identityReference也是购买vpn时回给你的吗?
    槛外人_:@EdwardSelf 对的。
  • 蜗牛1992:如果使用证书认证的话,p12证书可以植入代码安装吗?我把p12转成data设置了identityData,identityPassword也设置了,就是连接不上去。。。你有试过这种吗?
    ProgramDouglas:同问,这个问题解决了吗?
  • 少年灬听雨:楼主..你好...请问, 如果VPN服务器为网关, 我的设置里需要加入 PORT一项, 请问要加在什么地方?
    槛外人_:@少年灬听雨 不好意思, openvpn没怎么接触过,不过它是开源,可以google一下openvpn的iOS端实现。
    少年灬听雨:@槛外人_ 是openvpn的连接方式, 我有一个问题...如果服务端采用的是openvpn的方式..那么我在客户端一定也是openvpn吧. 现在要做关于openvpn的开发..要求在客户端继承openvpn, 这个现在应该是没发做到的吧.
    槛外人_:你用的是什么方式的VPN 连接。 没听说需要加端口啊
  • 闲云悠鹤蝶恋舞:楼主,你好,请问一下,APP 开启VPN 之后,然后把APP 杀死, 重新启动APP的时候,怎么判断VPN的连接状态,我是用[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnConnectionStatusChanged) name:NEVPNStatusDidChangeNotification object:nil];来监听的,但是杀死app后,再次重启app时,监听到的状态是NEVPNStatusInvalid,实际上vpn还在连接呢。请问楼主是怎么监听的?
    槛外人_:我用的就是这种监听方式,如果你监听道的状态一直是Invalid的,可能是哪里的设置不对,具体还得看代码
  • 伤心于泪:大神 我请问下 pptp的连接方式 可以嘛
    槛外人_:PPTP 的连接方式后来被苹果弃用,但是如上面朋友所说的能否从私有API调用就不得而知了,暂时还没有研究过
  • 故辰:您好,找了很多文章,发现这篇对我帮助最大。有一处问题不知道作者遇到过没有。我这边设置了 passwordReference 这个属性,从keychain 里面获取,但是手动在设置里面打开VPN的时候,还是需要输入密码。
    LEOHAI:楼主 请问您解决了吗? 我现在也是碰到这样的问题,每次都需要用户输入密码
    故辰:@槛外人_ NEVPNIKEAuthenticationMethodCertificate,我设置的是证书的方式;我不是很了解IOS开发,处于兴趣,做个App练手。后来觉得这个密码问题,应该是Keychain的访问权限问题,我设置的是nil,应该只有我写的应用可以读取密码,其他程序无法获取。:smile: 感谢作者。
    槛外人_:很高兴能帮助到你,不知道authenticationMethod参数你设置的是什么?
  • Sherlock_chan:楼主大神,请问一下,APP 开启VPN 之后,然后把APP 退出, 重新启动APP的时候,这么判断VPN的连接状态
    闲云悠鹤蝶恋舞:你好,请问这个问题解决了吗?
    槛外人_:过奖!
    首先你得监听VPN的通知,其中VPN有个状态NEVPNStatus,你得看这个状态是处于什么值,这样就知道了当前VPN是否已经处于连接了。
  • 51bitquant:你的广告选用什么平台的?
    对酒当歌的夜:@槛外人_ 可以自己加广告吗?带广告的app不是要被苹果收过路费吗
    槛外人_:inmobi 和 有米
  • 800cbcf2e3a3:你好,用NetworkExtension这个框架怎么实现L2TP类型的vpn协议呢? 或者不用这个框架,我该怎么实现L2TP类型的vpn连接呢?
    妳笇what:@yecaiCode 那你最终是用的什么私有API实现的可以采用L2PT类型协议的?求告知
    800cbcf2e3a3:关于L2TP,苹果也有关于它的头文件的,但是还是属于私有API,即使实现了也不可能上架。
    伤心于泪:你这个后来 实现了吗
  • 安浪创想:不会是开源的吧?开源的话贴地址哈学习摩拜下:heart_eyes:
    槛外人_:暂时还没有开源,其实关键点就是我上面说的那些:grin:

本文标题:iOS开发使用NetworkExtension框架编写VPN应用

本文链接:https://www.haomeiwen.com/subject/dwvctxtx.html