Ktor客户端IOS和自签名证书

3

我正在使用 Ktor 和 Kotlin/native 在 iOS 应用中访问内部开发服务器。该开发服务器使用一个由内部 CA 颁发的证书,该证书不被公开信任。

在尝试使用以下代码访问服务器时:

  internal suspend fun performHttp(url : String)
    {
        // URL is a self signed HTTPS: request
        val client = HttpClient(Ios) 

        val response = client.get<String>(url)
        println(response)
    }

会抛出以下异常:

TIC SSL Trust Error [32:0x281956dc0]: 3:0
esri2[470:136341] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9807)
esri2[470:136341] Task <F3CC4C40-0231-4E58-97F3-F457D5A18BB0>.<1> HTTP load failed (error code: -1202 [3:-9807])
 esri2[470:136417] Task <F3CC4C40-0231-4E58-97F3-F457D5A18BB0>.<1> finished with error - code: -1202
esri2[470:136211] Task <F3CC4C40-0231-4E58-97F3-F457D5A18BB0>.<1> load failed with error Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “server1.internal.lan” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x12b094e00) s: server1.internal.lan i: Internal-Issuing-CA2>",

我如何说服 Ktor 访问此 URL,或忽略不受信任的证书?是的,我知道不应该忽略不受信任的证书,但这只是一个实验测试。

看看这个,也许能解决你的问题: https://dev59.com/o10Z5IYBdhLWcg3wnBb_#40299837 - Aleksandr Honcharov
我已经尝试过了,但我在我的帖子中没有提到。我已将该域添加到NSExceptionDomains列表中,其中NSIncludesSubdomains = true和NSExceptionAllowsInsecureHTTPLoads = true。看起来Ktor没有使用该配置。到目前为止,解决方法是将内部根CA导出为pem文件,然后将其发送到我的iPhone上,安装并信任它(https://medium.com/collaborne-engineering/self-signed-certificates-in-ios-apps-ff489bf8b96e)。我希望在Ktor中有更好的解决方案。 - Trond Tunheim
嗨,你找到真正的解决方案了吗? - Thomas Vos
不,我没有。我仍然希望有一些更好的解决方案来解决这个问题。 - Trond Tunheim
1个回答

2
“Ktor iOS engine”提供了使用“IosClientEngineConfig.kt”来配置底层“NSURLSession”的能力。
通过这个能力,您可以在config中设置“handleChallenge”块,从而配置(除其他外)“ChallengeHandler”。
val client = HttpClient(Ios) {
    engine {
        handleChallenge(TrustSelfSignedCertificate())
    }
}

然后你需要在 Kotlin 中实现一个类,类似于这样:
internal data class TrustSelfSignedCertificate internal constructor(
    private val validateTrust: Boolean = true
) : ChallengeHandler {

    override fun invoke(
        session: NSURLSession,
        task: NSURLSessionTask,
        challenge: NSURLAuthenticationChallenge,
        completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Unit
    ) {
        val hostname = challenge.protectionSpace.host

        val serverTrust = challenge.protectionSpace.serverTrust
        var result: SecTrustResultType = 0u

        memScoped {
            val nativeResult = alloc<SecTrustResultTypeVar>()
            nativeResult.value = result
            SecTrustEvaluate(serverTrust!!, nativeResult.ptr)
        }

        val serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0)
        val serverCertificateData = SecCertificateCopyData(serverCertificate)
        val data = CFDataGetBytePtr(serverCertificateData)
        val size = CFDataGetLength(serverCertificateData)

        val cert1 = NSData.dataWithBytes(data, size.toULong())
        val pathToCert = NSBundle.mainBundle.pathForResource("myOwnCert", "cer")

        val localCertificate: NSData = NSData.dataWithContentsOfFile(pathToCert!!)!!

        if (localCertificate == cert1) {
            completionHandler(
                NSURLSessionAuthChallengeUseCredential,
                NSURLCredential.create(serverTrust)
            )
        } else {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, null)
        }
    }
}


另外,不要忘记将您的证书作为文件“myOwnCert.cer”放入iOS项目中(可能在顶层目录下)。
注意:
Ktor 使用 iOS 引擎时不遵循/使用NSApptransportSecurity
这段代码基于answers
博客文章的帮助下完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接