Ktor 和 Netty 控制 trustManager 或 sslContext

问题描述 投票:0回答:1

我正在尝试添加自定义逻辑以接受使用 Ktor 服务器和 Netty 到服务器的 tls 连接,我没有找到修改

trustManger
sslContext
的方法。

    val environment = applicationEngineEnvironment {
        connector {
            port = 8080
        }
        sslConnector(
            keyStore = keystore,
            keyAlias = "selfsigned",
            keyStorePassword = { "".toCharArray() },
            privateKeyPassword = { "changeit".toCharArray() }
        ) {
            port = 8443
            trustStore = clientkeystore
        }
        module {
            routing {
                get("/204") {
                    call.response.status(HttpStatusCode.NoContent)
                }
            }
        }
    }

    embeddedServer(Netty, environment).start(true)

这是使用密钥库的mtls,但我想控制接受客户端证书的逻辑,例如忽略客户端证书的有效性,这就是为什么我需要提供

trustManager

我查了源码,好像

sslContext
trustManger
是在NettyChannelInitializer中设置的

public class NettyChannelInitializer(
    private val enginePipeline: EnginePipeline,
    private val environment: ApplicationEngineEnvironment,
    private val callEventGroup: EventExecutorGroup,
    private val engineContext: CoroutineContext,
    private val userContext: CoroutineContext,
    private val connector: EngineConnectorConfig,
    private val requestQueueLimit: Int,
    private val runningLimit: Int,
    private val responseWriteTimeout: Int,
    private val requestReadTimeout: Int,
    private val httpServerCodec: () -> HttpServerCodec,
    private val channelPipelineConfig: ChannelPipeline.() -> Unit
) : ChannelInitializer<SocketChannel>() {
    private var sslContext: SslContext? = null

    init {
        if (connector is EngineSSLConnectorConfig) {

            // It is better but netty-openssl doesn't support it
//              val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
//              kmf.init(ktorConnector.keyStore, password)
//              password.fill('\u0000')

            @Suppress("UNCHECKED_CAST")
            val chain1 = connector.keyStore.getCertificateChain(connector.keyAlias).toList() as List<X509Certificate>
            val certs = chain1.toList().toTypedArray()
            val password = connector.privateKeyPassword()
            val pk = connector.keyStore.getKey(connector.keyAlias, password) as PrivateKey
            password.fill('\u0000')

            sslContext = SslContextBuilder.forServer(pk, *certs).apply {
                if (alpnProvider != null) {
                    sslProvider(alpnProvider)
                    ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                    applicationProtocolConfig(
                        ApplicationProtocolConfig(
                            ApplicationProtocolConfig.Protocol.ALPN,
                            ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
                            ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
                            ApplicationProtocolNames.HTTP_2,
                            ApplicationProtocolNames.HTTP_1_1
                        )
                    )
                }
                connector.trustManagerFactory()?.let { this.trustManager(it) }
            }
                .build()
        }
    }

有没有办法使用

channelPipelineConfig
configureBootstrap
配置逻辑?或者我需要创建自定义 netty 引擎?

我尝试过使用

childHandler
但它从未被调用过,也许我错过了一些东西。

    embeddedServer(Netty, port = 8443, configure = {
        configureBootstrap = {
            val certsChain = keystore2.getCertificateChain("selfsigned").toList() as List<X509Certificate>
            val certs = certsChain.toTypedArray()
            val password = "changeit".toCharArray()
            val privateKey = keystore2.getKey("selfsigned", password) as PrivateKey
            val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
                .also { it.init(keystore2) }



            childHandler(object : ChannelInitializer<SocketChannel>() {
                override fun initChannel(ch: SocketChannel) {
                    println("initChannel")
                    val sslContext = SslContextBuilder.forServer(privateKey, *certs)
                        .trustManager(trustManagerFactory)
                        .build()
                    val sslEngine = sslContext.newEngine(ch.alloc()).apply {
                        useClientMode = false
                        needClientAuth = false
                    }
                    ch.pipeline().addLast(SslHandler(sslEngine))
                }
            })
        }
    }, module = {
        routing {
            get("/204") {
                call.response.status(HttpStatusCode.NoContent)
            }
        }
    }).start(wait = true)
netty ktor mtls sslcontext trustmanager
1个回答
0
投票

我已经成功使用

channelPipelineConfig

将 sslContext 添加到 Netty
    embeddedServer(Netty, port = 8443, configure = {
    val certsChain = keystore2.getCertificateChain("selfsigned").toList() as List<X509Certificate>
    val certs = certsChain.toTypedArray()
    val password = "changeit".toCharArray()
    val privateKey = keystore2.getKey("selfsigned", password) as PrivateKey
    val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
        .also { it.init(keystore2) }


    channelPipelineConfig = {
        val sslContext = SslContextBuilder.forServer(privateKey, *certs)
            .trustManager(trustManagerFactory)
            .build()

        val sslEngine = sslContext.newEngine(channel().alloc()).apply {
            useClientMode = false
            needClientAuth = true
        }

        addFirst("ssl", SslHandler(sslEngine))
    }
}, module = {
    routing {
        get("/204") {
            call.response.status(HttpStatusCode.NoContent)
        }
    }
}).start(wait = true)
© www.soinside.com 2019 - 2024. All rights reserved.