如何在旧版本的放心中允许自签名 SSL 证书

Posted

技术标签:

【中文标题】如何在旧版本的放心中允许自签名 SSL 证书【英文标题】:How to allow self-signed SSL certificates in older version of rest-assured 【发布时间】:2017-06-13 05:21:35 【问题描述】:

必须使用旧版本 (1.8.1) 的放心框架。并且我从 rest-assured 调用的所有 post()get() 请求都失败了,因为我用于测试的服务器具有自签名 SSL 证书。

我知道从 2.2.0 版开始,设置 ssl 连接以使用 relaxedHTTPSValidation 非常容易,例如

given().relaxedHTTPSValidation().when().get("https://some_server.com")

我还在放心的wiki上找到了如何处理SSL config。

但我的问题是在旧版本的放心 (1.8.1) 中是否有可能。

抛出的异常是SSLHandshakeException

这是我得到的踪迹:

   javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
       at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
       at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
       at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
       at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
       at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
       at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
       at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
       at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
       at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
       at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
       at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
       at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
       at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:543)
       at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:409)
       at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
       at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:304)
       at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:611)
       at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:446)
       at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:882)
       at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
       at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
       at org.apache.http.client.HttpClient$execute.call(Unknown Source)
       at com.jayway.restassured.internal.RequestSpecificationImpl$RestAssuredHttpBuilder.doRequest(RequestSpecificationImpl.groovy:1377)
       at com.jayway.restassured.internal.http.HTTPBuilder.post(HTTPBuilder.java:341)
       at com.jayway.restassured.internal.http.HTTPBuilder$post.call(Unknown Source)
       at com.jayway.restassured.internal.RequestSpecificationImpl.sendRequest(RequestSpecificationImpl.groovy:801)
       at com.jayway.restassured.internal.RequestSpecificationImpl.this$2$sendRequest(RequestSpecificationImpl.groovy)
       at com.jayway.restassured.internal.RequestSpecificationImpl$this$2$sendRequest.call(Unknown Source)
       at com.jayway.restassured.internal.filter.RootFilter.filter(RootFilter.groovy:30)
       at com.jayway.restassured.filter.Filter$filter.call(Unknown Source)
       at com.jayway.restassured.internal.filter.FilterContextImpl.next(FilterContextImpl.groovy:49)
       at com.jayway.restassured.filter.FilterContext$next.call(Unknown Source)
       at com.jayway.restassured.internal.RequestSpecificationImpl.invokeFilterChain(RequestSpecificationImpl.groovy:758)
       at com.jayway.restassured.internal.RequestSpecificationImpl$invokeFilterChain.callCurrent(Unknown Source)
       at com.jayway.restassured.internal.RequestSpecificationImpl.applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy:1142)
       at com.jayway.restassured.internal.RequestSpecificationImpl.this$2$applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy)
       at com.jayway.restassured.internal.RequestSpecificationImpl$this$2$applyPathParamsAndSendRequest.callCurrent(Unknown Source)
       at com.jayway.restassured.internal.RequestSpecificationImpl.post(RequestSpecificationImpl.groovy:135)
       at com.ibm.datatools.webauto.framework.common.BaseWebTest.login(BaseWebTest.java:53)
       at com.ibm.datatools.webauto.dynamite.svt.ui.testcases.UDXTest.idaLogin(UDXTest.java:47)
       at com.ibm.datatools.webauto.dynamite.svt.ui.testcases.UDXTest.beforeClass(UDXTest.java:62)
       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
       at java.lang.reflect.Method.invoke(Method.java:498)
       at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:85)
       at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:510)
       at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:211)
       at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:138)
       at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:170)
       at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:104)
       at org.testng.TestRunner.privateRun(TestRunner.java:774)
       at org.testng.TestRunner.run(TestRunner.java:624)
       at org.testng.SuiteRunner.runTest(SuiteRunner.java:359)
       at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:354)
       at org.testng.SuiteRunner.privateRun(SuiteRunner.java:312)
       at org.testng.SuiteRunner.run(SuiteRunner.java:261)
       at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
       at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
       at org.testng.TestNG.runSuitesSequentially(TestNG.java:1215)
       at org.testng.TestNG.runSuitesLocally(TestNG.java:1140)
       at org.testng.TestNG.run(TestNG.java:1048)
       at org.testng.TestNG.privateMain(TestNG.java:1355)
       at org.testng.TestNG.main(TestNG.java:1324)

注意:这不是 this 或 this 问题的重复,因为那里提供的答案涉及较新版本的 rest-assured。

【问题讨论】:

您遇到了什么 SSL 错误?我猜是握手异常,但你能发布堆栈跟踪吗?另外,您是否尝试过将证书导入 Java 安装的信任库? @Adam:关于将证书导入信任库。这似乎不是一个选项,因为我们的测试环境是自动创建/生成的,所以我可以假设每个测试会话生成一次证书。是否可以在测试前下载此类证书,创建我可以在测试会话期间使用的 临时 信任存储并将此证书添加到该信任存储? 好吧,根据错误,看起来您需要在启动 JVM 时将证书上传到您的信任库中。见***.com/questions/373295/…。这应该是您的环境设置的一部分,如果由于某些可怕的官僚原因无法发生,我认为除了获得真正的签名证书或强制将其存储在您的测试中(不是推荐)。 @Adam: 是的,但是 通常每个测试会话我都会创建一个新的测试服务器和新的自签名证书。因此,我必须在测试会话之前添加每个这样的证书,并可能在测试会话之后将其删除。所以实际上它看起来非常像@BeforeSuite@AfterSuite 方法(设置和拆卸)的一部分。我现在正在尝试在 Java 中自动执行此操作,而无需使用外部工具。完成后可能会粘贴带有解决方案的代码 sn-p 作为答案。 【参考方案1】:

以下修正对我有用

@Test
public void testUserFetchesSuccess()
   given().config(RestAssured.config().sslConfig(new SSLConfig().allowAllHostnames())).
    get(restUrl).
    then().body("id", equalTo("abc0"));

【讨论】:

你用放心的1.8.1或更早的版本测试了吗?【参考方案2】:

正如我在我的一个 cmets 中所写,我尝试了以下解决方案:

    下载服务器证书 创建临时密钥库以供放心使用 将服务器证书存储在临时密钥库中 在测试期间连接到服务器时使用临时密钥库以保证安全

这是代码的重要部分:

protected Certificate[] getCertificates(String url) throws IOException, GeneralSecurityException

    URL urlConnection = new URL(url);
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(new KeyManager[0], new TrustManager[] new DummyTrustManager(), new SecureRandom());
    SSLContext.setDefault(context);

    HttpsURLConnection con = (HttpsURLConnection)urlConnection.openConnection();
    con.setHostnameVerifier(
            new HostnameVerifier() 
                public boolean verify(String hostname, SSLSession session) 
                    return true;
                    
    );

    con.setSSLSocketFactory(context.getSocketFactory());
    con.connect();
    Certificate[] certificates = con.getServerCertificates();

    con.disconnect();
    return certificates;



protected void storeCertificates(Certificate[] certs, String keystorePath, String keystorePassword) throws GeneralSecurityException, IOException

    KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
    keystore.load(null, keystorePassword.toCharArray());
    for (Certificate c: certs)
    
        keystore.setCertificateEntry(Integer.toHexString(c.hashCode()), c);
    
    File f = new File(keystorePath);
    FileOutputStream out_stream = new FileOutputStream(f);
    keystore.store(out_stream, keystorePassword.toCharArray());
    out_stream.close();

在那之后我唯一要做的就是:

 RestAssured.keystore(keystorePath, keystorePassword);

注意事项:

getCertificates 方法是基于piece of code 我在this 问题下找到的。 DummyTrustManager 与那里的DefaultTrustManager 完全相同。 storeCertificates 是基于 this 问题和它的答案。 我仍然不确定SSLContext.setDefault(context) 是暂时(在下载证书期间)还是永久更改SSLContext

【讨论】:

以上是关于如何在旧版本的放心中允许自签名 SSL 证书的主要内容,如果未能解决你的问题,请参考以下文章

如何创建一个自签名的SSL证书

如何创建一个自签名的SSL证书

如何创建一个自签名的SSL证书

如何创建一个自签名的SSL证书

如何创建一个自签名的SSL证书(X509)

Android使用SSL自签名证书