在Java Playframework 2.2中信任所有SSL证书

15

我正在尝试在Play框架中调用一个具有自签名SSL证书的Web服务,使用以下函数:

public static play.libs.F.Promise<Result> webcall() {
       String feedUrl = "https://10.0.1.1/client/api";
       final play.libs.F.Promise<Result> resultPromise = WS.url(feedUrl).get().map(
                    new Function<WS.Response, Result>() {
                        public Result apply(WS.Response response) {
                            return ok("Feed title:" + response.asJson().findPath("title").toString());
                        }
                    }
            );
            return resultPromise;
        }

在日志中出现以下错误:

[error] play - Cannot invoke the action, eventually got an error: java.net.ConnectException: General SSLEngine problem to https://10.0.1.1/client/api
[error] application - 

! @6fpimpnp6 - Internal server error, for (GET) [/webcall] ->

play.api.Application$$anon$1: Execution exception[[ConnectException: General SSLEngine problem to https://10.0.1.1/client/api]]
    at play.api.Application$class.handleError(Application.scala:293) ~[play_2.10.jar:2.2.0]
    at play.api.DefaultApplication.handleError(Application.scala:399) [play_2.10.jar:2.2.0]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$2$$anonfun$applyOrElse$3.apply(PlayDefaultUpstreamHandler.scala:261) [play_2.10.jar:2.2.0]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$2$$anonfun$applyOrElse$3.apply(PlayDefaultUpstreamHandler.scala:261) [play_2.10.jar:2.2.0]
    at scala.Option.map(Option.scala:145) [scala-library.jar:na]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$2.applyOrElse(PlayDefaultUpstreamHandler.scala:261) [play_2.10.jar:2.2.0]
java.net.ConnectException: General SSLEngine problem to https://10.0.1.1/client/api
    at com.ning.http.client.providers.netty.NettyConnectListener.operationComplete(NettyConnectListener.java:103) ~[async-http-client.jar:na]
    at org.jboss.netty.channel.DefaultChannelFuture.notifyListener(DefaultChannelFuture.java:427) ~[netty.jar:na]
    at org.jboss.netty.channel.DefaultChannelFuture.notifyListeners(DefaultChannelFuture.java:413) ~[netty.jar:na]
    at org.jboss.netty.channel.DefaultChannelFuture.setFailure(DefaultChannelFuture.java:380) ~[netty.jar:na]
    at org.jboss.netty.handler.ssl.SslHandler.setHandshakeFailure(SslHandler.java:1417) ~[netty.jar:na]
    at org.jboss.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1293) ~[netty.jar:na]
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
    at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1362) ~[na:1.7.0_40]
    at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:513) ~[na:1.7.0_40]
    at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:790) ~[na:1.7.0_40]
    at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758) ~[na:1.7.0_40]
    at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624) ~[na:1.7.0_40]
    at org.jboss.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1225) ~[netty.jar:na]
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) ~[na:1.7.0_40]
    at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1683) ~[na:1.7.0_40]
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:278) ~[na:1.7.0_40]
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) ~[na:1.7.0_40]
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341) ~[na:1.7.0_40]
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153) ~[na:1.7.0_40]
Caused by: 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.validator.PKIXValidator.doBuild(PKIXValidator.java:385) ~[na:1.7.0_40]
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) ~[na:1.7.0_40]
    at sun.security.validator.Validator.validate(Validator.java:260) ~[na:1.7.0_40]
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) ~[na:1.7.0_40]
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:283) ~[na:1.7.0_40]
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:138) ~[na:1.7.0_40]
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) ~[na:1.7.0_40]
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) ~[na:1.7.0_40]
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ~[na:1.7.0_40]
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) ~[na:1.7.0_40]
    at sun.security.validator.Validator.validate(Validator.java:260) ~[na:1.7.0_40]
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) ~[na:1.7.0_40]
如果我使用HttpsURLConnection调用服务,则只需添加即可正常工作。
TrustManager[] trustAllcerts = new TrustManager[]{
    new X509TrustManager() {

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            // TODO Auto-generated method stub

        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            // TODO Auto-generated method stub

        }
    }};

javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
sc.init(null, trustAllcerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier allHostsValid = new HostnameVerifier() {

    @Override
    public boolean verify(String arg0, SSLSession arg1) {
        // TODO Auto-generated method stub
        return false;
    }
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

如何在Play框架中信任所有自签名 / 不受信任的SSL证书?

4个回答

26

尝试将以下代码添加到conf/application.conf文件中

ws.acceptAnyCertificate=true

如果您正在使用 Promise 类,则此方法可行。但如果您通过其他的 HttpClient 调用服务,这种方法将不起作用。

更新:从 Play Framework 2.5 开始,您应该使用以下内容 -

play.ws.ssl.loose.acceptAnyCertificate=true

在开发环境中,您可以这样做,但是在生产环境中,您绝不能这样做,因为这可能会成为安全威胁。在生产环境中,应该将客户端的证书安装在您的密钥库中。


3
请不要这样做。您可以从Play TLS activator示例(http://typesafe.com/activator/template/play-tls-example)生成所需的证书,然后在ws.ssl的trustManager中添加cacerts(参见下面的示例)。请注意不要改变原意。 - Will Sargent
1
这在安全方面是很危险的。你不想盲目地接受任何证书。 - jetcom
1
从play 2.5开始,您可以使用play.ws.ssl.loose.acceptAnyCertificate=true。但是如前所述,这可能非常危险,请在使用此功能之前阅读https://www.playframework.com/documentation/2.5.x/LooseSSL。 - Benoit

7
请不要接受所有证书 -- 您可以使用信任管理器来处理您的自定义证书,然后回退到默认的信任存储。
ws.ssl {
  trustManager = {
     stores = [
      { path: ${store.directory}/exampletrust.jks }     # Added trust store
      { path: ${java.home}/lib/security/cacerts } # Fallback to default JSSE trust store
     ]
  }
}

请参见http://www.playframework.com/documentation/2.3.x/ExampleSSLConfig以获取更多详细信息。

1
我是Play框架的新手,想知道store.directory具体在哪里?如何使用自定义信任存储库进行打包?我将其放在conf中,并使用路径'conf/exampletrust.jks'进行引用。这样做是否能够正确地打包应用程序? - kldavis4
1
没问题。请参考 https://github.com/wsargent/play-ws-with-letsencrypt/blob/master/conf/ws.conf 以获取一些自定义证书的示例。 - Will Sargent
看起来如果信任存储使用相对路径,它将相对于应用程序启动的位置。是否有一个变量可以在配置中使用,以引用部署应用程序的根目录,以便可以部署信任存储而不使用绝对路径? - kldavis4
1
您可以使用HOCON替换并回退到环境变量来定义绝对路径 https://github.com/typesafehub/config/blob/master/HOCON.md#substitution-fallback-to-environment-variables,例如 ${?ABSOLUTE_PATH} 等。 - Will Sargent

4

只需将您要信任的证书安装到信任存储中。

不要盲目接受所有证书,这会使您的应用程序容易受到中间人攻击。

即使仅用于开发,这也是人们在最后期限时忘记删除的代码和设置。

您可能遇到的下一个问题是主机名匹配。您在https://10.0.1.1/中使用了IP地址,因此您的证书应该具有针对此IP地址的SAN条目,而不仅仅是其CN中的IP地址。更多详细信息请参见此处。不要使用您在代码中设置的HostnameVerifier(出于同样的原因)。


感谢您宝贵的建议。 - asvignesh

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接