无法使用网络扩展框架设置 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;
}