Can't setup VPN connection using Network Exten

2019-03-30 18:21发布

问题:

So I've been trying to use the iOS 8 Network Extension Framework to setup a VPN connection when the users presses a UIButton. I've used the following tutorial: http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/

But for some reason it keeps asking for the vpn password and shared secret. Even though I set the passwordReference and sharedSecretReference. And if I enter these details when installing the profile it will still not work. It just doesn't do anything when starting the connection using the framework. When trying to connect using the settings app it gives a "there's no sharedSecret" error.

This is the code I use to set up the connection.

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")
                        }
                    }
                })
            }
        }
    }
}

These are the functions I use as a keychain reference.

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
}

Any help is appreciated!

回答1:

So I had to eventually replace the Swift library I used to access the keychain with the following Obj-C methods. This solved my problem as far as I'm currently able to tell.

+ (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;
}