Hello :wave: . I am currently using Ktor in an and...
# squarelibraries
s
Hello šŸ‘‹ . I am currently using Ktor in an android context. My use case is to communicate to a server with SSL certificate authentication. I have this part set up in my
OkHttpClient
using a custom
SSLSocketFactory
that handles all the certificates and private keys. However, one thing i need to support now, is adding an
ALPN
ā€œprotocol nameā€. Is there a way i can configure my client to specify the ā€œALPN protocol nameā€
For more context, this is related to aws-iot core, and the documentation states this:
Clients that connect on port 443 with X.509 client certificate authentication must implement the Application Layer Protocol Negotiation (ALPN) TLS extension and use the ALPN protocol name listed in the ALPN ProtocolNameList sent by the client as part of the
ClientHello
message.
y
Not using clean public API. OkHttp sets this without making it configurable.
The code path is https://github.com/square/okhttp/blob/ff8da5fa0d7735f012105317b03571a776efa4fc/okh[…]p/src/jvmMain/kotlin/okhttp3/internal/connection/ConnectPlan.kt calls (on modern Android) https://github.com/square/okhttp/blob/ff8da5fa0d7735f012105317b03571a776efa4fc/okh[…]lin/okhttp3/internal/platform/android/Android10SocketAdapter.kt which calls val sslParameters = sslSocket.sslParameters // Enable ALPN. sslParameters.applicationProtocols = Platform.alpnProtocolNames(protocols).toTypedArray() sslSocket.sslParameters = sslParameters
What you could try, is a connection spec with supportsTlsExtensions = false (this is deprecated), and then setting what you want in a decorating sslSocketFactory.
But you are fighting OkHttp, it doesn't want to let you set this.
It's probably worth raising a feature request to add this.
s
thanks for the hints, i’ll try this out in a few days and get back to you if i have encounter any issues
j
Do you need a custom name? Or just HTTP/2?
x-amzn-mqtt-ca, mqtt
j
That’s super bad
Why use web sockets if you’re not using HTTP?
s
I think MQTT uses websockets because it is publisher/subscriber architecture. In my case I'll be using http (ALPN x-amzn-http-ca HTTP)
j
Yeah, I guess it’s new to me to advertise something other than the standard HTTP transports with OkHttp
I dig using mqtt when that’s the transport, but I don’t understand using a custom thing for regular web sockets
y
If a custom sslSocketFactory is needed anyway. Then maybe the suggestion about disabling supportsTlsExtensions is a valid option
j
I think that mechanism is currently very much an implementation detail
s
i tried to set the
applicationProtocols
in my
sslSocketFactory
like:
sslContext._defaultSSLParameters.applicationProtocols = arrayOf_("x-amzn-http-ca")
but that seems to not have an effect. Is there a way to supply it to okhttp otherwise?
y
Make a repro if you can, it's hard to see if all the right pieces are in place.
j
Can you get a client library from Amazon? You’re best to use their weird clients with their weird servers
šŸ˜… 2
🄲 2
s
@yschimke here some simplified sample code where i tried your hints:
Copy code
fun sampleSocketFactory(): SSLSocketFactory =
    SSLContext.getInstance("TLS").apply {
        // simplified
        val keyManagers = emptyArray<KeyManager>()
        val trustManagers = emptyArray<TrustManager>()
        init(keyManagers, trustManagers, null)
        val params = defaultSSLParameters
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            params.applicationProtocols += (arrayOf("x-amzn-http-ca"))
        }
    }.socketFactory

private val sampleOkHttpClient = HttpClient(OkHttp) {
    install(ContentNegotiation) {
        json(JsonFormat)
    }

    engine {
        config {
            connectionSpecs(
                listOf(ConnectionSpec.MODERN_TLS.apply {
                    val f = ConnectionSpec::class.java.getDeclaredField("supportsTlsExtensions")
                    f.isAccessible = true
                    f.set(this, false)
                })
            )

            sslSocketFactory(
                sampleSocketFactory(),
                // implementation removed for simplicity
                myX509TrustManager,
            )
        }
    }
}
@jessewilson i was hoping not to go back to their weird libraries 🄲
one thing i did find while looking around at their libraries was this decorated SSLSocketFactory, perhaps it could work for this case if i use it together with my existing factory and pass that to okhttp
j
I’d like it if you can make that work
I’m reluctant to get OkHttp to support custom protocol names in ALPN. OkHttp advertises itself as an HTTP/1 and HTTP/2 client, and that’s what it is. I expect that by using custom ALPN strings Amazon is going to break all the other general-purpose HTTP things like Postman and curl
I get that they’d have custom ALPN keys for MQTT over TLS
But for web sockets? It’s really just HTTP/1 at the protocol layer
Or rather, the web socket negotiation is an HTTP thing, not a TLS thing
s
yeah i totally understand, i’ve never encountered a need for custom protocol names until now. I’ll do some more testing on Monday and come back with the results i get
thank you color 1
y
I don't think you need reflection, I was thinking more like this https://github.com/square/okhttp/pull/8081
šŸ™Œ 1
@jessewilson Maybe it just needs to be renamed? I think I'm more aligned to having workaround options, where OkHttp doesn't override specific configuration apps make. If we didn't we wouldn't expose sslSocketFactory. We do, we just override a valid thing that the factory has set.
j
That’s a fantastic idea and I’m on board
s
massive thanks @yschimke that worked flawlessly šŸ™Œ
the only downside i see is that
parameters.applicationProtocols = arrayOf("x-amzn-http-ca")
seems to require API level 29
y
Yep, you can check the other code paths.
There is one for pre 10
This is the tradeoff
It's all on you to configure
OkHttp tries to hide all of this and just do the right default thing on all android versions
s
Yeah that’s a fair tradeoff, i’m massively grateful for your help, at least i can make my way from here šŸ™Œ
Would you want me to submit a feature request? something along the lines of ā€œDo not override configurations specified by clientsā€ along with this example?
y
I made one earlier today
šŸ™Œ 2
s
Awesome! Thank you šŸ™Œ