我正在尝试使用NETWORK EXTENSION框架连接VPN,但它始终要求我输入密码。我不知道为什么因为看起来好像从Keychain成功了,也许我错了。这是我的代码:
func createVPN() {
let manager: NEVPNManager = NEVPNManager.sharedManager()
let keychain = KeychainSwift()
manager.loadFromPreferencesWithCompletionHandler { (error: NSError?) -> Void in
if (error != nil) {
print("error")
} else {
let p = NEVPNProtocolIKEv2()
p.remoteIdentifier = "*******"
p.username = "*******"
p.passwordReference = keychain.getData("vpnpassword")!
p.serverAddress = "free-nl.hide.me"
p.authenticationMethod = NEVPNIKEAuthenticationMethod.None
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
manager.`protocol` = p
manager.enabled = true
manager.saveToPreferencesWithCompletionHandler({ (error: NSError?) -> Void in
if (error != nil) {
print(error)
} else {
do {
try NEVPNManager.sharedManager().connection.startVPNTunnel()
} catch {
print("can't connect VPN'")
}
}
})
}
}
}
P.S。当我自己输入密码时,它可以正常工作。
而且我也无法理解为什么createVPN函数只有在从viewDidLoad调用时才有效,这种方式不是:
@IBAction func initVPN(sender: AnyObject) {
self.createVPN()
}
SOLUTION:
问题出在Keychain
解决方案是使用objc函数来保存和获取数据:
+ (void) storeData: (NSString * )key data:(NSData *)data {
NSLog(@"Store Data");
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
[dict setObject:data forKey:(__bridge id)kSecValueData];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
if(errSecSuccess != status) {
NSLog(@"Unable add item with key =%@ error:%d",key,(int)status);
}
}
+ (NSData *) getData: (NSString *)key {
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:@"VPN" forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
[dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnPersistentRef];
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);
if( status != errSecSuccess) {
NSLog(@"Unable to fetch item for key %@ with error:%d",key,(int)status);
return nil;
}
NSData *resultData = (__bridge NSData *)result;
return resultData;
}