解决WireMock自签名SSL的javax.net.ssl.SSLHandshakeException错误
解决WireMock自签SSL证书主机名不匹配问题
核心原因
WireMock默认生成的自签证书CN为Tom Akehurst,而Java的SSL握手会严格校验请求主机名(localhost)与证书中的CN/SAN(Subject Alternative Name)是否一致,因此即使证书已导入信任库,仍会抛出javax.net.ssl.SSLHandshakeException: No name matching localhost found错误。Chrome/Postman因校验策略宽松可正常访问,但Java客户端不会忽略该不匹配。
解决方案1:生成自定义自签证书(推荐)
生成包含localhost作为CN和SAN的自签证书,替换WireMock默认证书:
步骤1:生成自定义keystore
使用JDK自带的keytool生成符合要求的证书:
keytool -genkeypair -alias wiremock-localhost -keyalg RSA -keysize 2048 -validity 3650 -keystore wiremock-localhost.jks -dname "CN=localhost, OU=Dev, O=Local, L=City, ST=State, C=CN" -ext SAN=DNS:localhost,DNS:127.0.0.1 -storepass password -keypass password
-ext SAN=DNS:localhost,DNS:127.0.0.1:添加SAN扩展,同时支持localhost和127.0.0.1两种访问方式storepass和keypass可自定义,需记住后续配置使用
步骤2:导入证书到信任库
将生成的证书导入Java信任库(推荐使用自定义信任库,避免修改系统文件):
# 生成自定义信任库 keytool -importkeystore -srckeystore wiremock-localhost.jks -destkeystore truststore.jks -srcstorepass password -deststorepass password # 若需导入系统默认jssecacerts(需管理员权限) keytool -importcert -alias wiremock-localhost -file wiremock-localhost.cer -keystore $JAVA_HOME/jre/lib/security/jssecacerts -storepass changeit
步骤3:配置WireMock使用自定义证书
在JUnit 4测试类中指定自定义keystore参数:
import com.github.tomakehurst.wiremock.WireMockServer; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; public class WireMockSslTest { private WireMockServer wireMockServer; @Before public void setup() { wireMockServer = new WireMockServer(options() .port(8089) // HTTP端口 .httpsPort(8443) // HTTPS端口 .keystorePath("wiremock-localhost.jks") // 自定义keystore路径 .keystorePassword("password") // keystore密码 .keyManagerPassword("password")); // 密钥密码 wireMockServer.start(); } @After public void teardown() { wireMockServer.stop(); } }
解决方案2:临时绕过主机名校验(仅测试环境)
如果只是临时测试需求,可通过Java代码禁用SSL主机名校验(绝对禁止在生产环境使用):
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; public class SslUtils { public static void disableHostnameVerification() throws Exception { // 创建信任所有证书的TrustManager TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; // 初始化SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); // 禁用主机名校验 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); } }
在Java客户端发起请求前执行SslUtils.disableHostnameVerification()即可跳过校验。
解决方案3:修改WireMock默认证书(不推荐)
若不想生成新证书,可手动替换WireMock Jar包中的默认证书文件,但操作复杂且易因版本更新失效,不推荐使用。
内容的提问来源于stack exchange,提问作者tarekahf
相关产品推荐
相关产品推荐

