https://kotlinlang.org logo
#squarelibraries
Title
# squarelibraries
s

samuel

10/27/2023, 3:38 PM
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

yschimke

10/27/2023, 4:54 PM
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

samuel

10/27/2023, 8:45 PM
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

jessewilson

10/28/2023, 11:55 AM
Do you need a custom name? Or just HTTP/2?
x-amzn-mqtt-ca, mqtt
j

jessewilson

10/28/2023, 12:19 PM
That’s super bad
Why use web sockets if you’re not using HTTP?
s

samuel

10/28/2023, 1:33 PM
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

jessewilson

10/28/2023, 2:23 PM
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

yschimke

10/28/2023, 2:30 PM
If a custom sslSocketFactory is needed anyway. Then maybe the suggestion about disabling supportsTlsExtensions is a valid option
j

jessewilson

10/28/2023, 2:47 PM
I think that mechanism is currently very much an implementation detail
s

samuel

10/28/2023, 4:34 PM
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

yschimke

10/28/2023, 5:58 PM
Make a repro if you can, it's hard to see if all the right pieces are in place.
j

jessewilson

10/28/2023, 8:26 PM
Can you get a client library from Amazon? You’re best to use their weird clients with their weird servers
😅 2
🥲 2
s

samuel

10/29/2023, 12:25 AM
@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

jessewilson

10/29/2023, 12:53 AM
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

samuel

10/29/2023, 2:45 AM
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

yschimke

10/29/2023, 11:21 AM
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

jessewilson

10/29/2023, 12:00 PM
That’s a fantastic idea and I’m on board
s

samuel

10/29/2023, 12:42 PM
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

yschimke

10/29/2023, 1:05 PM
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

samuel

10/29/2023, 1:32 PM
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

yschimke

10/29/2023, 1:51 PM
I made one earlier today
🙌 2
s

samuel

10/29/2023, 2:06 PM
Awesome! Thank you 🙌
8 Views