无法使用Swift在iOS 8中使用网络扩展框架设置VPN连接

5
所以我一直在尝试使用iOS 8网络扩展框架来设置VPN连接,当用户按下UIButton时。我使用了以下教程:http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/ 但是由于某种原因,它一直要求输入VPN密码和共享密钥。即使我已经设置了passwordReference和sharedSecretReference。如果我在安装配置文件时输入这些详细信息,它仍然无法工作。当使用该框架启动连接时,它什么也不做。尝试使用设置应用程序连接时,会出现“没有共享密钥”的错误。
以下是我用于设置连接的代码。
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
}

非常感谢您的帮助!

1个回答

2

因此,我最终不得不使用以下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;
}

嘿,当手机进入自动锁定睡眠状态时,它会断开连接吗? - MD Singh
如果您将 p.disconnectOnSleep 设置为 true,则它不会断开连接。 - Houwert
"userName" 和 "serverAddress" 的值是什么? - Bkillnest
我尝试了您的代码,但是当我点击连接按钮时,收到了日志错误“操作无法完成。(NEVPNErrorDomain错误1)”。 - Bkillnest
@Houwert 我添加了这些 Objective-C 方法,但是我该如何存储数据?// KeychainItemWrapper.storeData("shared", data: config.psk!) 我的 PSK 是字符串类型,但 storeData 接受的是 'Data' 而不是字符串值。我该如何解决这个问题? - Bartu Akman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接