简体   繁体   中英

Connecting to VPN programmatically keeps asking for system keychain credentials

My code connects to VPN using NEVPNManager and certificate (on MacOS), the code works good but whenever I try to connect ( targetManager.connection.startVPNTunnel() ) the system prompt for system keychain credentials.

macos提示您输入系统钥匙串凭证

Is there to make this alert go away after the first approval?

Code:

func initVPNTunnelProviderManager(vpnConfig: Vpn, _ connect: Bool = false) {
    let url = URL(string: vpnConfig.certUrl!)
    do {
        let certData = try Data(contentsOf: url!)

        let targetManager: NEVPNManager = NEVPNManager.shared()
        targetManager.loadFromPreferences(completionHandler: { (error:Error?) in
            if let error = error {
                print(error)
            }

            switch targetManager.connection.status {
            case NEVPNStatus.connected:
                targetManager.connection.stopVPNTunnel()
                break
            case NEVPNStatus.disconnected:
                let ip = vpnConfig.serverUrl

                let providerProtocol = NEVPNProtocolIKEv2()
                providerProtocol.authenticationMethod = .certificate
                providerProtocol.serverAddress = ip
                providerProtocol.remoteIdentifier = ip
                providerProtocol.localIdentifier = "myIdentifier"

                providerProtocol.useExtendedAuthentication = false
                providerProtocol.ikeSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES128GCM
                providerProtocol.ikeSecurityAssociationParameters.diffieHellmanGroup = .group19
                providerProtocol.ikeSecurityAssociationParameters.integrityAlgorithm = .SHA512
                providerProtocol.ikeSecurityAssociationParameters.lifetimeMinutes = 20

                providerProtocol.childSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES128GCM
                providerProtocol.childSecurityAssociationParameters.diffieHellmanGroup = .group19
                providerProtocol.childSecurityAssociationParameters.integrityAlgorithm = .SHA512
                providerProtocol.childSecurityAssociationParameters.lifetimeMinutes = 20

                providerProtocol.deadPeerDetectionRate = .medium
                providerProtocol.disableRedirect = true
                providerProtocol.disableMOBIKE = false
                providerProtocol.enableRevocationCheck = false
                providerProtocol.enablePFS = true
                providerProtocol.useConfigurationAttributeInternalIPSubnet = false

                providerProtocol.serverCertificateCommonName = ip
                providerProtocol.serverCertificateIssuerCommonName = ip
                providerProtocol.disconnectOnSleep = true
                providerProtocol.identityDataPassword = vpnConfig.certPassword
                providerProtocol.certificateType = .ECDSA256
                providerProtocol.identityData = certData

                targetManager.protocolConfiguration = providerProtocol
                targetManager.localizedDescription = vpnConfig.name
                targetManager.isEnabled = true
                targetManager.isOnDemandEnabled = false


                targetManager.saveToPreferences(completionHandler: { (error:Error?) in
                    if let error = error {
                        print(error)
                    } else {
                        print("Save successfully")
                        if connect {
                            do {
                                try targetManager.connection.startVPNTunnel()
                            } catch {
                                print("Failed to connect")
                            }
                        }
                    }
                })
                break
            default:
                print("connection status not handled: \(targetManager.connection.status.rawValue)")
            }
        })
    } catch {
        print(error.localizedDescription)
    }
}

}

The workaround is to not use identityData and identityDataPassword but instead import the identity into the user's keychain yourself (using SecItemImport ) and then pass a persistent reference to the identity to NEVPNManager via the identityReference property.
Here's a working sample:

private func identityReference(for pkcs12Data: Data, password: String) -> Data {

    var importResult: CFArray? = nil
    let err = SecPKCS12Import(pkcs12Data as NSData, [
        kSecImportExportPassphrase: password
    ] as NSDictionary, &importResult)
    guard err == errSecSuccess else { fatalError() }
    let importArray = importResult! as! [[String:Any]]
    let identity = importArray[0][kSecImportItemIdentity as String]! as! SecIdentity

    var copyResult: CFTypeRef? = nil
    let err2 = SecItemCopyMatching([
        kSecValueRef: identity,
        kSecReturnPersistentRef: true
    ] as NSDictionary, &copyResult)
    guard err2 == errSecSuccess else { fatalError() }
    return copyResult! as! Data
}

func initVPNTunnelProviderManager(vpnConfig: Vpn, _ connect: Bool = false) {
let url = URL(string: vpnConfig.certUrl!)
do {
    let certData = try Data(contentsOf: url!)

    let targetManager: NEVPNManager = NEVPNManager.shared()
    targetManager.loadFromPreferences(completionHandler: { (error:Error?) in
        if let error = error {
            print(error)
        }

        switch targetManager.connection.status {
        case NEVPNStatus.connected:
            targetManager.connection.stopVPNTunnel()
            break
        case NEVPNStatus.disconnected:
            let ip = vpnConfig.serverUrl

            let providerProtocol = NEVPNProtocolIKEv2()
            providerProtocol.authenticationMethod = .certificate
            providerProtocol.serverAddress = ip
            providerProtocol.remoteIdentifier = ip
            providerProtocol.localIdentifier = "myIdentifier"

            providerProtocol.useExtendedAuthentication = false
            providerProtocol.ikeSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES128GCM
            providerProtocol.ikeSecurityAssociationParameters.diffieHellmanGroup = .group19
            providerProtocol.ikeSecurityAssociationParameters.integrityAlgorithm = .SHA512
            providerProtocol.ikeSecurityAssociationParameters.lifetimeMinutes = 20

            providerProtocol.childSecurityAssociationParameters.encryptionAlgorithm = .algorithmAES128GCM
            providerProtocol.childSecurityAssociationParameters.diffieHellmanGroup = .group19
            providerProtocol.childSecurityAssociationParameters.integrityAlgorithm = .SHA512
            providerProtocol.childSecurityAssociationParameters.lifetimeMinutes = 20

            providerProtocol.deadPeerDetectionRate = .medium
            providerProtocol.disableRedirect = true
            providerProtocol.disableMOBIKE = false
            providerProtocol.enableRevocationCheck = false
            providerProtocol.enablePFS = true
            providerProtocol.useConfigurationAttributeInternalIPSubnet = false

            providerProtocol.serverCertificateCommonName = ip
            providerProtocol.serverCertificateIssuerCommonName = ip
            providerProtocol.disconnectOnSleep = true
            providerProtocol.identityReference = self.identityReference(for: certData, password: vpnConfig.certPassword!)
            providerProtocol.certificateType = .ECDSA256

            targetManager.protocolConfiguration = providerProtocol
            targetManager.localizedDescription = vpnConfig.name
            targetManager.isEnabled = true
            targetManager.isOnDemandEnabled = false


            targetManager.saveToPreferences(completionHandler: { (error:Error?) in
                if let error = error {
                    print(error)
                } else {
                    print("Save successfully")
                    if connect {
                        do {
                            try targetManager.connection.startVPNTunnel()
                        } catch {
                            print("Failed to connect")
                        }
                    }
                }
            })
            break
        default:
            print("connection status not handled: \(targetManager.connection.status.rawValue)")
        }
    })
} catch {
    print(error.localizedDescription)
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM