Play 2.6.x服务器未请求客户端证书问题求助
嘿,我之前帮不少开发者排查过Play框架里客户端认证的问题,结合你说的情况(自签名证书、跟着play-tls-example做但服务器没请求证书),咱们一步步来定位问题:
1. 先检查SSLEngine的客户端认证开关
这是最容易踩的坑——你的CustomSSLEngineProvider里,创建SSLEngine时必须明确开启客户端证书请求。我猜你可能漏了这关键一行:
override def createSSLEngine(): SSLEngine = { val engine = sslContext.createSSLEngine() engine.setNeedClientAuth(true) // 就是这个!没加的话服务器根本不会要客户端证书 engine.setUseClientMode(false) // 服务器模式必须设为false,默认可能不是 // 下面的协议和 cipher suite 配置可选,但建议加上确保兼容性 engine.setEnabledProtocols(Array("TLSv1.2", "TLSv1.3")) engine.setEnabledCipherSuites(engine.getSupportedCipherSuites.filter(_.contains("ECDHE"))) engine }
如果没加setNeedClientAuth(true),服务器完全不会触发客户端证书请求流程,这是90%的人会犯的错。
2. 确认TrustStore配置没出错
因为你用的是自签名客户端证书,服务器的TrustStore必须信任这个证书(要么直接导入客户端公钥,要么导入签发它的CA证书)。你得确保在CustomSSLEngineProvider里正确加载了TrustStore:
private def loadTrustStore(): KeyStore = { val conf = appProvider.get.configuration val trustStorePath = conf.get[String]("play.server.https.trustStore.path") val trustStorePass = conf.get[String]("play.server.https.trustStore.password").toCharArray val trustStore = KeyStore.getInstance(KeyStore.getDefaultType) val inputStream = Files.newInputStream(FileSystems.getDefault.getPath(trustStorePath)) trustStore.load(inputStream, trustStorePass) inputStream.close() trustStore }
然后用这个TrustStore初始化TrustManagerFactory,不然服务器拿到客户端证书也没法验证,甚至可能在握手阶段就跳过请求(不过更常见的是请求后验证失败)。
3. 检查application.conf的配置是否到位
Play 2.6需要明确指定你的自定义SSLEngineProvider,同时基础的HTTPS配置也得开:
play.server.https.port = 9443 # 这里的类路径要和你的包名完全一致 play.server.ssl.engineProvider = "https.CustomSSLEngineProvider" # 服务器自身的密钥库和信任库配置 play.server.https.keyStore.path = "/path/to/your/server.jks" play.server.https.keyStore.password = "your-server-pass" play.server.https.trustStore.path = "/path/to/your/truststore.jks" play.server.https.trustStore.password = "your-trust-pass"
要是类路径写错了,Play会默认用自带的SSLEngineProvider,那你的自定义配置就完全没生效,自然不会请求客户端证书。
4. 验证客户端证书的使用是否正确
别光怪服务器,也得确认客户端能正确发送证书。比如用curl测试的话:
curl -v --cert client.p12:client-cert-password https://localhost:9443/your-test-endpoint
如果客户端没带证书,服务器会直接拒绝连接,但如果服务器连请求都没发,那还是回到第一步的问题。
5. 开SSL调试日志找细节
要是上面几步都没问题,就开JVM的SSL调试日志看看握手过程:
-Djavax.net.debug=ssl,handshake
启动服务器后,找日志里的CertificateRequest条目——如果有,说明服务器确实请求了客户端证书,问题在客户端或TrustStore;如果没有,那肯定是SSLEngine的配置没生效。
6. 确认你的CustomSSLEngineProvider被加载了
有时候可能因为包名拼写错误,Play没加载你的自定义Provider。启动服务器时搜一下日志里的CustomSSLEngineProvider,要是没找到相关加载日志,就检查play.server.ssl.engineProvider的配置路径是否和你的类完全匹配。
总的来说,最常见的问题就是漏加了setNeedClientAuth(true),先检查这一步,再依次排查其他点,应该能解决你的问题。
内容的提问来源于stack exchange,提问作者erip

