无法使用网络扩展框架设置 VPN 连接 iOS 8 in Swift
Can't setup VPN connection using Network Extension Framework iOS 8 in Swift
所以我一直在尝试使用 iOS 8 网络扩展框架在用户按下 UIButton 时设置 VPN 连接。
我使用了以下教程:http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/
但出于某种原因,它一直要求提供 vpn 密码和共享机密。即使我设置了 passwordReference 和 sharedSecretReference。
如果我在安装配置文件时输入这些详细信息,它仍然无法工作。它只是在使用框架启动连接时不执行任何操作。尝试使用设置应用程序连接时出现 "there's no sharedSecret" 错误。
这是我用来建立连接的代码。
func toggleConnection(sender: UIButton) {
if(!self.connected){
self.manager.loadFromPreferencesWithCompletionHandler { (error) -> Void in
if((error) != nil) {
println("VPN Preferences error: 1")
}
else {
var p = NEVPNProtocolIPSec()
p.username = "$username"
p.serverAddress = "$vpn"
p.passwordReference = KeychainService.dataForKey("vpnPassword")!
println(p.passwordReference)
p.authenticationMethod = NEVPNIKEAuthenticationMethod.SharedSecret
p.sharedSecretReference = KeychainService.dataForKey("sharedSecret")!
println(p.sharedSecretReference)
p.localIdentifier = "vpn"
p.remoteIdentifier = "vpn"
p.disconnectOnSleep = false
self.manager.`protocol` = p
self.manager.onDemandEnabled = true
self.manager.localizedDescription = "VPN"
self.manager.saveToPreferencesWithCompletionHandler({ (error) -> Void in
if((error) != nil) {
println("VPN Preferences error: 2")
println(error)
}
else {
var startError: NSError?
self.manager.connection.startVPNTunnelAndReturnError(&startError)
if((startError) != nil) {
println("VPN Preferences error: 3")
println(startError)
}
else {
println("Start VPN")
}
}
})
}
}
}
}
这些是我用作钥匙串参考的函数。
class func save(service: NSString, key: String, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, key, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
SecItemDelete(keychainQuery as CFDictionaryRef)
if data == "" { return }
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
println(status)
}
class func load(service: NSString, key: String) -> NSData? {
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, key, kCFBooleanTrue, kSecMatchLimitOne, kCFBooleanTrue], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit, kSecReturnPersistentRef])
var dataTypeRef :Unmanaged<AnyObject>?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
println(status)
if (status != errSecSuccess) {
return nil
}
let opaque = dataTypeRef?.toOpaque()
var contentsOfKeychain: NSData? = nil
if let op = opaque {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
contentsOfKeychain = retrievedData
}
println(contentsOfKeychain)
return contentsOfKeychain
}
感谢任何帮助!
所以我不得不最终用以下 Obj-C 方法替换我用来访问钥匙串的 Swift 库。据我所知,这解决了我的问题。
+ (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;
}
所以我一直在尝试使用 iOS 8 网络扩展框架在用户按下 UIButton 时设置 VPN 连接。 我使用了以下教程:http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/
但出于某种原因,它一直要求提供 vpn 密码和共享机密。即使我设置了 passwordReference 和 sharedSecretReference。 如果我在安装配置文件时输入这些详细信息,它仍然无法工作。它只是在使用框架启动连接时不执行任何操作。尝试使用设置应用程序连接时出现 "there's no sharedSecret" 错误。
这是我用来建立连接的代码。
func toggleConnection(sender: UIButton) {
if(!self.connected){
self.manager.loadFromPreferencesWithCompletionHandler { (error) -> Void in
if((error) != nil) {
println("VPN Preferences error: 1")
}
else {
var p = NEVPNProtocolIPSec()
p.username = "$username"
p.serverAddress = "$vpn"
p.passwordReference = KeychainService.dataForKey("vpnPassword")!
println(p.passwordReference)
p.authenticationMethod = NEVPNIKEAuthenticationMethod.SharedSecret
p.sharedSecretReference = KeychainService.dataForKey("sharedSecret")!
println(p.sharedSecretReference)
p.localIdentifier = "vpn"
p.remoteIdentifier = "vpn"
p.disconnectOnSleep = false
self.manager.`protocol` = p
self.manager.onDemandEnabled = true
self.manager.localizedDescription = "VPN"
self.manager.saveToPreferencesWithCompletionHandler({ (error) -> Void in
if((error) != nil) {
println("VPN Preferences error: 2")
println(error)
}
else {
var startError: NSError?
self.manager.connection.startVPNTunnelAndReturnError(&startError)
if((startError) != nil) {
println("VPN Preferences error: 3")
println(startError)
}
else {
println("Start VPN")
}
}
})
}
}
}
}
这些是我用作钥匙串参考的函数。
class func save(service: NSString, key: String, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, key, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])
SecItemDelete(keychainQuery as CFDictionaryRef)
if data == "" { return }
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
println(status)
}
class func load(service: NSString, key: String) -> NSData? {
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, key, kCFBooleanTrue, kSecMatchLimitOne, kCFBooleanTrue], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit, kSecReturnPersistentRef])
var dataTypeRef :Unmanaged<AnyObject>?
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
println(status)
if (status != errSecSuccess) {
return nil
}
let opaque = dataTypeRef?.toOpaque()
var contentsOfKeychain: NSData? = nil
if let op = opaque {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
contentsOfKeychain = retrievedData
}
println(contentsOfKeychain)
return contentsOfKeychain
}
感谢任何帮助!
所以我不得不最终用以下 Obj-C 方法替换我用来访问钥匙串的 Swift 库。据我所知,这解决了我的问题。
+ (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;
}