以编程方式连接到VPN不断询问系统密钥链凭据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了以编程方式连接到VPN不断询问系统密钥链凭据相关的知识,希望对你有一定的参考价值。

我的代码使用NEVPNManager和证书(在MacOS上)连接到VPN,代码工作正常,但每当我尝试连接(targetManager.connection.startVPNTunnel())系统提示输入系统密钥链时。

macos prompt for system keychain credentials

第一次批准后是否会使此警报消失?

码:

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

}

答案

解决方法是不使用identityDataidentityDataPassword,而是自己将身份导入用户的钥匙串(使用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)
}
}

以上是关于以编程方式连接到VPN不断询问系统密钥链凭据的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式连接 LDAP 并在 AEM 中验证凭据

windows7怎么设置vpn

华为防火墙网关间通过IKE方式协商IPSec VPN隧道(共享密钥认证)

如何以编程方式访问连接到 Windows 桌面的 iOS 设备的文件系统?

以编程方式连接到蓝牙

Node.js - 以编程方式连接到 *** 或通过 *** 路由 HTTP 请求