可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need to crawl internal company site that has expired/self-signed certificate. Noone is ever going to configure valid certificate for that host, so I have to use insecure connection.
curl
has --insecure
flag for that purpose,
Scala finagle library has .tlsWithoutValidation()
mode.
QUESTION: Is there a Kotlin library that has similar option?
UPD: so far I am using Fuel with the javish workaround found here but still searching for better ways..
fun useInsecureSSL() { // Create a trust manager that does not validate certificate chains val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager { override fun getAcceptedIssuers(): Array<X509Certificate>? = null override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) = Unit override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) = Unit }) val sc = SSLContext.getInstance("SSL") sc.init(null, trustAllCerts, java.security.SecureRandom()) HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory) // Create all-trusting host name verifier val allHostsValid = HostnameVerifier { _, _ -> true } // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid) }
The above workaround works however it is too verbose and seems to set insecure mode for every connection made by my app, not only for the particular one.
回答1:
Fuel allows you to create your own instance of the Fuel client through the FuelManager
class. The manager lets you can set your own HostnameVerifier
and SSLSocketFactory
and then creates a client with those configured. See https://github.com/kittinunf/Fuel/blob/1.16.0/fuel/src/main/kotlin/com/github/kittinunf/fuel/core/FuelManager.kt#L31-L43
val manager : FuelManager = FuelManager().apply { val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager { override fun getAcceptedIssuers(): Array<X509Certificate>? = null override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) = Unit override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) = Unit }) socketFactory = SSLContext.getInstance("SSL").apply { init(null, trustAllCerts, java.security.SecureRandom()) }.socketFactory hostnameVerifier = HostnameVerifier { _, _ -> true } }
Then to check that only connections that goes through this custom FuelManager
is untrusted and connections that don't are trusted, we do this:
val (_, untrustedResp, untrustedResult) = manager.request(Method.GET, "https://internal/company/site").response() assert(untrustedResp.statusCode == 200) val (bytes, _) = untrustedResult assert(bytes != null) val (_, trustedResp, trustedResult) = "https://internal/company/site".httpGet().response() assert(trustedResp.statusCode != 200) val (bytes, error) = trustedResult assert(bytes == null) println(error) // javax.net.ssl.SSLHandshakeException: PKIX path building failed: ...
The custom FuelManager
was able to make the request successfully because it trusts all certs but for the connection that didn't use the custom manager, we can see that it returns with javax.net.ssl.SSLHandshakeException
.
回答2:
Thanks, @ludenus, @Jason Yeo.
If you use Fuel and want to overcome old SSL certificate problem, then in your Api-class where you keep all requests, write what they suggested. This solution doesn't check any URL, it allows requests to any URLs.
import com.github.kittinunf.fuel.core.FuelManager import com.github.kittinunf.fuel.core.Method import com.github.kittinunf.fuel.core.Request import com.github.kittinunf.fuel.httpPost import java.security.cert.X509Certificate import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager class ApiImpl : Api { init { FuelManager.instance.basePath = Api.SERVER_URL // Your usual URL. FuelManager.instance.apply { val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager { override fun getAcceptedIssuers(): Array<X509Certificate>? = null override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) = Unit override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) = Unit }) socketFactory = SSLContext.getInstance("SSL").apply { init(null, trustAllCerts, java.security.SecureRandom()) }.socketFactory hostnameVerifier = HostnameVerifier { _, _ -> true } } } /** * Your requests stay intact. */ override fun getSomething(id: Int): Request = "getinfo/".httpPost(listOf("id" to id)) }