在Swift中使用Network Extension Framework iOS 8的VPN连接无法正常工作

时间:2015-06-22 17:21:17

标签: ios swift

当用户按下UIButton时,我一直在尝试使用iOS 8网络扩展框架来设置VPN连接。我使用了以下教程:http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/。我也查看了这篇文章Can't setup VPN connection using Network Extension Framework iOS 8 in Swift,这与我得到的行为相同。当我运行应用程序时,我会在安装配置文件时提示输入vpn密码和共享密码,即使它们已在代码中设置了所有其他必需值。如果我在安装配置文件时输入这些详细信息,它仍然无效。当尝试使用应用程序进行连接时,它会出现“没有sharedSecret”错误。在引用的帖子中,问题显然是通过重写保存和访问OBJ-C中的钥匙串数据的代码来解决的。我想让它在swift中工作或理解为什么它在swift中不起作用。

这是连接的代码

let manager = NEVPNManager.sharedManager()
@IBAction func connectToVpn(sender: AnyObject) {
  println("in call vpn")
           manager.loadFromPreferencesWithCompletionHandler { (error) -> Void in
             if((error) != nil) {
                 println("VPN Preferences error: 1")
               }
              else {
                    var p = NEVPNProtocolIPSec()
                    p.username = "billy"
                    p.serverAddress = "xxx.xxx.xxx.xxx"
                    p.passwordReference = self.loadkeychain("vpnpassword")
                    println(p.passwordReference)
                    p.authenticationMethod = NEVPNIKEAuthenticationMethod.SharedSecret
                    p.sharedSecretReference = self.loadkeychain("sharedSecret")
                    println(p.sharedSecretReference)
                    p.localIdentifier = "vpn"
                    p.remoteIdentifier = "vpn"
                    p.disconnectOnSleep = false
                    println("everything is set")
                    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")
                            }
                        }
                    })
                }
            }
        }

这是从钥匙串保存和检索的代码

func savekeychain(key: String, value: String) -> Bool {
         let valueData = value.dataUsingEncoding(NSUTF8StringEncoding,
            allowLossyConversion: false)
           let service = NSBundle.mainBundle().bundleIdentifier!
                let secItem = [
                      kSecClass as! String :
                      kSecClassGenericPassword as! String,
                      kSecAttrService as! String : service,
                      kSecAttrAccount as! String : key,
                       kSecValueData as! String : valueData!,
        ]
           var result: Unmanaged<AnyObject>? = nil
            let status = Int(SecItemAdd(secItem, &result))
                switch status{
                  case Int(errSecSuccess):
                  println("Successfully stored the value")
                   case Int(errSecDuplicateItem):
                   println("This item is already saved. Cannot duplicate it")
        default:
            println("An error occurred with code \(status)")
        }
                return true
    }

    func loadkeychain(keyToSearchFor: String) -> NSData 
        let service = NSBundle.mainBundle().bundleIdentifier!
         let query = [
          kSecClass as! String :
           kSecClassGenericPassword as! String,
            kSecAttrService as! String : service,
            kSecAttrAccount as! String : keyToSearchFor,
            kSecReturnData as! String : kCFBooleanTrue,
        ]
       var data: NSData!
        var returnedData: Unmanaged<AnyObject>? = nil
        let results = Int(SecItemCopyMatching(query, &returnedData))
         if results == Int(errSecSuccess){
              data = returnedData!.takeRetainedValue() as! NSData
               let value = NSString(data: data, encoding: NSUTF8StringEncoding)
               println("Value = \(value)")
                println("DATA = \(data)")
                    } else {
                  println("Error happened with code: \(results)")
        }
                     return data
    }

根据引用的帖子的建议,更改了我从保存和检索钥匙串中的数据到OBJ-C方法的功能,这确实解决了这些问题。一些测试表明swift和OBJ-C方法都返回了相同的值,所以我不确定为什么swift方法会导致所述行为。我注意到的另一件事是将值保存到钥匙串似乎有点不稳定,如果你删除一个键,然后用不同的值重新添加它似乎不起作用,需要将钥匙串重置为默认值。我仍然想弄清楚为什么swift方法似乎无法正常工作。

1 个答案:

答案 0 :(得分:1)

我知道我来晚了,但是它可能会帮助我们需要secItemCopyMatchig中的共享机密引用的人,您需要添加kSecReturnPersistentRef并将其设置为true。 下面的方块可能会帮助您。

enum VPNKeychain {

    /// Returns a persistent reference for a generic password keychain item, adding it to
    /// (or updating it in) the keychain if necessary.
    ///
    /// This delegates the work to two helper routines depending on whether the item already
    /// exists in the keychain or not.
    ///
    /// - Parameters:
    ///   - service: The service name for the item.
    ///   - account: The account for the item.
    ///   - password: The desired password.
    /// - Returns: A persistent reference to the item.
    /// - Throws: Any error returned by the Security framework.

    static func persistentReferenceFor(service: String, account: String, password: Data) throws -> Data {
        var copyResult: CFTypeRef? = nil
        let err = SecItemCopyMatching([
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: account,
            kSecReturnPersistentRef: true,
            kSecReturnData: true
            ] as NSDictionary, &copyResult)
        switch err {
        case errSecSuccess:
            return try self.persistentReferenceByUpdating(copyResult: copyResult!, service: service, account: account, password: password)
        case errSecItemNotFound:
            return try self.persistentReferenceByAdding(service: service, account:account, password: password)
        default:
            try throwOSStatus(err)
            // `throwOSStatus(_:)` only returns in the `errSecSuccess` case.  We know we're
            // not in that case but the compiler can't figure that out, alas.
            fatalError()
        }
    }

    /// Returns a persistent reference for a generic password keychain item by updating it
    /// in the keychain if necessary.
    ///
    /// - Parameters:
    ///   - copyResult: The result from the `SecItemCopyMatching` done by `persistentReferenceFor(service:account:password:)`.
    ///   - service: The service name for the item.
    ///   - account: The account for the item.
    ///   - password: The desired password.
    /// - Returns: A persistent reference to the item.
    /// - Throws: Any error returned by the Security framework.

    private static func persistentReferenceByUpdating(copyResult: CFTypeRef, service: String, account: String, password: Data) throws -> Data {
        let copyResult = copyResult as! [String:Any]
        let persistentRef = copyResult[kSecValuePersistentRef as String] as! NSData as Data
        let currentPassword = copyResult[kSecValueData as String] as! NSData as Data
        if password != currentPassword {
            let err = SecItemUpdate([
                kSecClass: kSecClassGenericPassword,
                kSecAttrService: service,
                kSecAttrAccount: account,
                ] as NSDictionary, [
                    kSecValueData: password
                    ] as NSDictionary)
            try throwOSStatus(err)
        }
        return persistentRef
    }

    /// Returns a persistent reference for a generic password keychain item by adding it to
    /// the keychain.
    ///
    /// - Parameters:
    ///   - service: The service name for the item.
    ///   - account: The account for the item.
    ///   - password: The desired password.
    /// - Returns: A persistent reference to the item.
    /// - Throws: Any error returned by the Security framework.

    private static func persistentReferenceByAdding(service: String, account: String, password: Data) throws -> Data {
        var addResult: CFTypeRef? = nil
        let err = SecItemAdd([
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: account,
            kSecValueData: password,
            kSecReturnPersistentRef: true,
            ] as NSDictionary, &addResult)
        try throwOSStatus(err)
        return addResult! as! NSData as Data
    }

    /// Throws an error if a Security framework call has failed.
    ///
    /// - Parameter err: The error to check.

    private static func throwOSStatus(_ err: OSStatus) throws {
        guard err == errSecSuccess else {
            throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
        }
    }
}