Spring-Kotlin应用打包exe后出现SSLHandshakeException的原因及解决
问题
我有一个Spring-Kotlin独立应用,直接运行应用或启动jar包时一切正常,可正常连接Supabase PostgreSQL数据库。但使用以下jpackage脚本生成exe并运行时:
commandLine( "$javaHome/bin/jpackage", "--type", "app-image", "--input", "build/libs", "--main-jar", jarPath, "--name", appName, "--dest", outputDir, "--app-version", version.toString(), "--icon", "src/main/resources/car_icon.ico", "--java-options", "-Djava.awt.headless=false", "--java-options", "-Dlogging.file=app.log", "--resource-dir", "src/main/resources", "--runtime-image", layout.buildDirectory.dir("my-runtime").get().asFile.absolutePath )
出现如下错误:
Caused by: org.postgresql.util.PSQLException: SSL error: Received fatal alert: insufficient_security at org.postgresql.ssl.MakeSSL.convert(MakeSSL.java:53) at org.postgresql.core.v3.ConnectionFactoryImpl.enableSSL(ConnectionFactoryImpl.java:631) at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:201) at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:268) at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:54) at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:273) at org.postgresql.Driver.makeConnection(Driver.java:446) at org.postgresql.Driver.connect(Driver.java:298) at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:98) at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:126) at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:46) at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:126) ... 146 more Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: insufficient_security at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:365) at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293) at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:204) at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172) at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506) at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421) at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455) at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426) at org.postgresql.ssl.MakeSSL.convert(MakeSSL.java:51) ... 163 more
为何仅运行exe时会出现该错误?该如何解决?
原因分析
核心问题是jpackage使用的自定义runtime镜像缺少SSL加密所需的关键组件。直接运行jar时依赖完整的JDK/JRE,包含所有默认安全提供者和加密套件;但通过--runtime-image指定的自定义运行时镜像,在构建过程中可能被自动裁剪掉了SSL/TLS相关的必要模块或加密算法,导致无法满足Supabase PostgreSQL要求的安全级别,从而触发insufficient_security错误。
解决方法
1. 确保自定义runtime包含完整安全模块
使用jlink构建runtime时,显式添加SSL相关的核心模块,避免被裁剪:
jlink --add-modules java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.xml.crypto \ --output my-runtime \ --strip-debug \ --no-man-pages \ --no-header-files
其中java.security.jgss和java.xml.crypto是SSL加密的关键依赖模块,必须包含。
2. 添加Java安全参数指定兼容的SSL配置
在jpackage的--java-options中添加SSL相关参数,强制使用Supabase支持的协议和加密套件:
commandLine( // ... 保留原有参数 "--java-options", "-Djava.awt.headless=false", "--java-options", "-Dlogging.file=app.log", "--java-options", "-Djdk.tls.client.protocols=TLSv1.2,TLSv1.3", "--java-options", "-Djavax.net.ssl.trustStore=${System.getProperty("java.home")}/lib/security/cacerts" )
Supabase PostgreSQL默认支持TLSv1.2和TLSv1.3,显式指定可避免协议不匹配问题。
3. 更新PostgreSQL JDBC驱动版本
确保使用最新版本的PostgreSQL驱动(推荐42.6.x及以上),旧版本可能存在与新版JDK runtime的SSL兼容问题。在build.gradle.kts中更新依赖:
implementation("org.postgresql:postgresql:42.6.0")
4. 验证runtime的安全配置
检查自定义runtime镜像中lib/security/java.security文件,确认以下安全提供者配置存在:
security.provider.1=sun.security.provider.Sun security.provider.2=sun.security.rsa.SunRsaSign security.provider.3=sun.security.ec.SunEC security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider
缺少这些配置会导致无法加载必要的加密算法。
内容的提问来源于stack exchange,提问作者TechGuy

