Search code examples
objective-cswiftmacosnetworkextension

IKEv2 password reference from keychain


I am trying to implement IKEv2 vpn connection on Mac OSX using NetworkExtension framework. Every time I get a popup to enter my vpn connection password. According to NEVPNProtocol specification password can be supplied by providing persistent reference of password stored in keychain. But it doesn't work. I've tried the same IKEv2 configuration code in iOS and it works perfectly.

I wrote a sample application to demonstrate how I do it - https://github.com/kestutisbalt/osx-ikev2-sample

How password is stored in keychain:

class func set(key: String, value: String) {

    let query: [NSObject: AnyObject] = [
        kSecValueData: value.dataUsingEncoding(NSUTF8StringEncoding)!,
        kSecClass: kSecClassGenericPassword,
        kSecAttrGeneric: key,
        kSecAttrAccount: key,
        kSecAttrAccessible: kSecAttrAccessibleAlways,
        kSecAttrService: NSBundle.mainBundle().bundleIdentifier!
    ]

    clear(key)
    SecItemAdd(query as CFDictionaryRef, nil)
}

How persistent reference is retrieved from keychain:

class func persistentRef(key: String) -> NSData? {
    let query: [NSObject: AnyObject] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrGeneric: key,
        kSecAttrAccount: key,
        kSecAttrAccessible: kSecAttrAccessibleAlways,
        kSecMatchLimit: kSecMatchLimitOne,
        kSecAttrService: NSBundle.mainBundle().bundleIdentifier!,
        kSecReturnPersistentRef: kCFBooleanTrue
    ]

    var secItem: AnyObject?
    let result = SecItemCopyMatching(query, &secItem)
    if result != errSecSuccess {
        return nil
    }

    return secItem as? NSData
}

IKEv2 configuration:

private func createIKEv2Protocol(host: String,
    username: String, password: String) -> NEVPNProtocolIKEv2 {

    Keychain.set(username, value: password)
    let passwordRef = Keychain.persistentRef(username)
    if passwordRef == nil {
        log("Failed to query password persistent ref")
    }

    let config = NEVPNProtocolIKEv2()

    config.remoteIdentifier = host
    config.serverAddress = host
    config.useExtendedAuthentication = true
    config.username = username
    config.passwordReference = passwordRef

    return config
}

Solution

  • Apple developer support replied to my question:

    The issue with your code is that it’s attempting to set up VPN in an unsupported way. Specifically, IKEv2 VPN does not support password-based authentication. The options you have for IKEv2 are listed in the NEVPNIKEAuthenticationMethod enum, namely, .Certificate and .SharedSecret.`