При программном подключении к VPN постоянно запрашиваются учетные данные системной связки ключей

Мой код подключается к VPN с помощью NEVPNManager и сертификат (на MacOS), код работает хорошо, но всякий раз, когда я пытаюсь подключиться (targetManager.connection.startVPNTunnel()) системная подсказка для системных учетных данных цепочки для ключей.

macos запрашивает учетные данные системной цепочки для ключей

Можно ли сделать так, чтобы это предупреждение исчезло после первого одобрения?

Код:

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

}

1 ответ

Обходной путь не использовать identityData а также identityDataPassword но вместо этого импортируйте личность в цепочку ключей пользователя самостоятельно (используя SecItemImport) и затем передать постоянную ссылку на личность NEVPNManager через identityReference имущество.
Вот рабочий образец:

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)
}
}
Другие вопросы по тегам