Rest Assured配置SSL证书连接报方法不存在及bad_certificate错误
Rest Assured 双向SSL连接配置问题排查与解决
问题现象
需要基于Rest Assured搭建带客户端证书认证的SSL测试请求,初始实现代码运行失败,初始代码如下:
public class ApiTests { @BeforeAll static void setUp() { RestAssured.baseURI = "my host"; } @Test void createUserTest() throws Exception { KeyStore keyStore = KeyStore.getInstance("PKCS12"); String keyStorePass = "my pass"; try (FileInputStream fis = new FileInputStream("D:\Idea_projects\TestApi\src\test\resources\my_cert.p12")) { keyStore.load(fis, keyStorePass.toCharArray()); } given() .keyStore(keyStore) .trustStore("C:\Program Files\Java\jdk-11.0.13\lib\security\cacerts", "changeit") .contentType(JSON) .when() .post("users") .then() .statusCode(200) .body("name", is("Valentin")) .body("job", is("qa")); } }
运行上述代码首先抛出方法不存在异常:
No signature of method: io.restassured.config.SSLConfig.keyStore() is applicable for argument types: (java.security.KeyStore) values: [java.security.KeyStore@504497fa] Possible solutions: keyStore(java.lang.String), keyStore(java.io.File, java.lang.String), keyStore(java.lang.String, java.lang.String), getKeyStore(), trustStore(java.security.KeyStore) groovy.lang.MissingMethodException: No signature of method: io.restassured.config.SSLConfig.keyStore() is applicable for argument types: (java.security.KeyStore) values: [java.security.KeyStore@504497fa] Possible solutions: keyStore(java.lang.String), keyStore(java.io.File, java.lang.String), keyStore(java.lang.String, java.lang.String), getKeyStore(), trustStore(java.security.KeyStore)
后续尝试调用relaxedHTTPSValidation()跳过SSL校验,又抛出SSL握手失败异常:
Received fatal alert: bad_certificate javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
该接口为双向TLS认证场景,服务端会强制校验客户端证书合法性,跳过证书校验的方式无法完成握手。
错误根因
- 方法调用错误:从异常提示可以明确,Rest Assured的
SSLConfig.keyStore()方法没有支持直接传入java.security.KeyStore单实例参数的重载,现有重载仅支持传入证书路径、证书File对象+密码的参数形式,只有trustStore()方法支持直接传入单KeyStore实例,因此直接传手动加载的KeyStore对象会抛出方法不存在异常。 - 方案选型错误:
relaxedHTTPSValidation()的作用是关闭客户端对服务端证书的合法性校验,并不会自动携带客户端证书给服务端,在双向TLS场景下服务端收不到合法客户端证书会直接返回bad_certificate告警,握手必然失败。 - 潜在路径问题:原代码中Windows路径使用单反斜杠,会被Java识别为转义字符,存在路径读取失败的隐患。
可行解决方案
方案1:直接使用Rest Assured内置的KeyStore加载逻辑(推荐)
不需要手动写流加载p12证书,直接调用keyStore的路径+密码重载方法,由Rest Assured内部完成证书加载,修正后代码如下:
public class ApiTests { @BeforeAll static void setUp() { RestAssured.baseURI = "my host"; } @Test void createUserTest() throws Exception { // Windows路径要么用双反斜杠转义,要么直接用正斜杠避免转义问题 String clientCertPath = "D:/Idea_projects/TestApi/src/test/resources/my_cert.p12"; String clientCertPass = "my pass"; String trustStorePath = "C:/Program Files/Java/jdk-11.0.13/lib/security/cacerts"; String trustStorePass = "changeit"; given() .keyStore(clientCertPath, clientCertPass) .trustStore(trustStorePath, trustStorePass) .contentType(JSON) .when() .post("users") .then() .statusCode(200) .body("name", is("Valentin")) .body("job", is("qa")); } }
方案2:自定义SSLConfig传入手动加载的KeyStore
如果有特殊需求必须手动加载KeyStore实例(比如证书从内存读取、需要自定义KeyManager逻辑),可以手动构造SSLConfig配置传入,不要直接在given()链式调用里传单KeyStore实例:
public class ApiTests { @BeforeAll static void setUp() { RestAssured.baseURI = "my host"; } @Test void createUserTest() throws Exception { // 手动加载客户端证书 KeyStore keyStore = KeyStore.getInstance("PKCS12"); String keyStorePass = "my pass"; try (FileInputStream fis = new FileInputStream("D:/Idea_projects/TestApi/src/test/resources/my_cert.p12")) { keyStore.load(fis, keyStorePass.toCharArray()); } // 手动构造SSL配置 SSLConfig sslConfig = new SSLConfig() .keyStore(keyStore, keyStorePass) // 该重载支持传入KeyStore+密码参数 .trustStore("C:/Program Files/Java/jdk-11.0.13/lib/security/cacerts", "changeit") .allowAllHostnames(); given() .config(RestAssured.config().sslConfig(sslConfig)) .contentType(JSON) .when() .post("users") .then() .statusCode(200) .body("name", is("Valentin")) .body("job", is("qa")); } }
排查校验点
- 确认客户端p12证书本身有效、密码正确,且已经被服务端信任,未过期、未被吊销
- 如果服务端使用自签证书,需要提前把服务端证书导入到指定的trustStore中,否则会报服务端证书不信任错误
- 双向TLS场景不要依赖
relaxedHTTPSValidation()解决问题,该方法仅适用于服务端证书不受信任但不需要客户端认证的测试场景
内容的提问来源于stack exchange,提问作者Valentin
相关产品推荐
相关产品推荐

