【问题标题】:How to bypass Https SSLHandshakeException with Tomee如何使用 Tomee 绕过 Https SSLHandshakeException
【发布时间】:2015-10-30 10:42:02
【问题描述】:

我正在尝试从tomee 1.6 server 调用WS 而不是SSL,但我得到了SSLHandshakeError。问题是证书是自签名的,我的JVM 无法识别。由于它仅用于测试目的,而不是生产,我被要求绕过证书控制。 我阅读了很多关于如何进行的资料,并且我已经编写了该代码:

一个类NaiveSSLContext

package fr.csf.ssl;

import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import javax.net.ssl.X509TrustManager;

/**
 * A factory class which creates an {@link SSLContext} that
 * naively accepts all certificates without verification.
 */
public class NaiveSSLContext
{
    private NaiveSSLContext()
    {}

    /**
     * Get an SSLContext that implements the specified secure
     * socket protocol and naively accepts all certificates
     * without verification.
     */
    public static SSLContext getInstance( String protocol) throws NoSuchAlgorithmException
    {
        SSLContext sslCtx = SSLContext.getInstance( protocol);
        init( sslCtx);
        return sslCtx;
    }

    /**
     * Get an SSLContext that implements the specified secure
     * socket protocol and naively accepts all certificates
     * without verification.
     */
    public static SSLContext getInstance( String protocol, Provider provider) throws NoSuchAlgorithmException
    {
        SSLContext sslCtx = SSLContext.getInstance( protocol, provider);
        init( sslCtx);
        return sslCtx;
    }

    /**
     * Get an SSLContext that implements the specified secure
     * socket protocol and naively accepts all certificates
     * without verification.
     */
    public static SSLContext getInstance( String protocol, String provider) throws NoSuchAlgorithmException, NoSuchProviderException
    {
        SSLContext sslCtx = SSLContext.getInstance( protocol, provider);
        init( sslCtx);
        return sslCtx;
    }

    /**
     * Set NaiveTrustManager to the given context.
     */
    private static void init( SSLContext context)
    {
        try
        {
            // Set NaiveTrustManager.
            context.init( null, new TrustManager[] { new NaiveTrustManager() }, new java.security.SecureRandom());
            System.out.println( "------------- Initialisation du NaiveSSLContext ---------------------");
        }
        catch( java.security.KeyManagementException e)
        {
            throw new RuntimeException( "Failed to initialize an SSLContext.", e);
        }
    }

    /**
     * A {@link TrustManager} which trusts all certificates naively.
     */
    private static class NaiveTrustManager implements X509TrustManager
    {
        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            System.out.println( "------------- NaiveTrustManager.getAcceptedIssuers() ---------------------");
            return null;
        }

        @Override
        public void checkClientTrusted( X509Certificate[] certs, String authType)
        {
            System.out.println( "------------- NaiveTrustManager.checkClientTrusted( " + certs.toString() + ", " + authType
                    + ") ---------------------");
        }

        @Override
        public void checkServerTrusted( X509Certificate[] certs, String authType)
        {
            System.out.println( "------------- NaiveTrustManager.checkServerTrusted( " + certs.toString() + ", " + authType
                    + ") ---------------------");
        }
    }
}

还有另一个类NaiveSSLSocketFactory

package fr.csf.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;

public class NaiveSSLSocketFactory extends javax.net.ssl.SSLSocketFactory
{
    private javax.net.ssl.SSLSocketFactory factory;

    public NaiveSSLSocketFactory() throws NoSuchAlgorithmException
    {
        javax.net.ssl.SSLContext sslCtx = NaiveSSLContext.getInstance( "SSL");
        factory = sslCtx.getSocketFactory();
    }

    private final String[] enabledProtocols = new String[]
    { "SSLv3", "TLSv1" };

    @Override
    public Socket createSocket( Socket s, String host, int port, boolean autoClose) throws IOException
    {
        Socket socket = factory.createSocket( s, host, port, autoClose);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( String host, int port) throws IOException, UnknownHostException
    {
        Socket socket = factory.createSocket( host, port);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( InetAddress host, int port) throws IOException
    {
        Socket socket = factory.createSocket( host, port);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException
    {
        Socket socket = factory.createSocket( host, port, localHost, localPort);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
    {
        Socket socket = factory.createSocket( address, port, localAddress, localPort);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public String[] getDefaultCipherSuites()
    {
        String[] cipherSuites = factory.getDefaultCipherSuites();
        return cipherSuites;
    }

    @Override
    public String[] getSupportedCipherSuites()
    {
        String[] cipherSuites = factory.getSupportedCipherSuites();
        return cipherSuites;
    }
}

问题是我不知道如何让 JVM 使用我的 Naive* 类而不是默认类。我尝试了不同的方法,但它们都不起作用:

第一次尝试:

javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory( new NaiveSSLSocketFactory());

我在 checkClientTruted 方法中的日志跟踪从不显示。似乎我的 NaiveSSLSocketFactory 从未被调用过。

第二次尝试:

java.security.Security.setProperty( "ssl.SocketFactory.provider", new NaiveSSLSocketFactory().getClass().getName());

由于 ClassLoader 问题,我遇到了 ClassNotFoundException,但在解决此问题后,同样的问题仍然存在。

我最终找到了一个博客,上面说 CXF 客户端必须做更多的配置工作:

<http-conf:conduit name="*.http-conduit" >
    <http-conf:tlsClientParameters 
        useHttpsURLConnectionDefaultSslSocketFactory="true"     
    />
</http-conf:conduit>

由于我使用的是 Tomee1.6 服务器,所以我的程序是一个 CXF 客户端。所以这一定是解决方案。但是我必须在哪里写这个配置属性?我在 Tomee 中找不到任何与 CXF 相关的 xml 文件。只有一个 cxf.properties 文件,几乎是空的。

【问题讨论】:

  • 不是重复的。我不问如何编写绕过证书控制的类。我已经写了它们,并在我的帖子中提供了它们。我认为它们是正确的,因为它们看起来就像我在专业网站上找到的一样。我问我如何告诉我的服务器(Tomeeplus v1.6)使用它们。由于它使用 CXF,我相信有一个配置属性需要更改,但我不知道是哪个,也不知道它在哪里。

标签: java web-services ssl cxf apache-tomee


【解决方案1】:

首先,Tomcat 不涉及您对 Web 服务的消费 - 事实上,它实际上不涉及您的应用程序进行的任何出站连接。

我知道两种方法可以实现 CXF 提供的所需结果,并且不会影响在同一 JVM 上运行的任何其他出站 SSL 连接:

  1. 将自签名证书添加到 CXF 客户端的管道信任 商店,或
  2. 在 CXF 上安装“无操作”信任管理器 客户端的 TLS 参数

第一种方法更可取,因为第二种方法将信任您的客户端连接的任何端点。

要实施第一种方法,请创建一个包含您希望信任的证书的密钥库(最好包括任何中间证书)。然后按照 CXF 手册部分 Configuring SSL Support 中的说明添加此信任库。您的管道配置将如下所示:

<http:conduit name="{http://apache.org/hello_world}HelloWorld.http-conduit">

  <http:tlsClientParameters>
    <sec:trustManagers>
      <sec:keyStore type="JKS" password="password"
                  file="my/file/dir/Truststore.jks"/>
    </sec:trustManagers>
  </http:tlsClientParameters>
  <http:client AutoRedirect="true" Connection="Keep-Alive"/>
</http:conduit>

请注意,上面示例中的管道名称显然只是一个示例。有关如何指定管道名称的另一个问题,请参阅the update to my answer here。另请注意,我没有包含密码套件过滤器,因为我相信它会默认为一组值,如果您使用的是 Java 6 或更早版本,这可能是不安全的......但这是另一个话题。

此外,您可以完全避开 CXF 的 Spring 配置,并使用 CXF 客户端 API 以编程方式完成上述所有操作。

我还强烈建议使用KeyStore Explorer 之类的工具从目标端点提取证书(和中介)并将它们导入新的信任库。

最后,关于您的初始解决方案,我想指出在 JVM 范围内安装 JDK API 支持的 SSL 套接字工厂和信任管理器等东西的危险。在支持多个应用程序的容器内运行时,这样做可能会产生危险后果:您可以破坏其他应用程序的安全配置文件。使用像 CXF 这样的框架的好处之一是它提供了为每个应用程序客户端(或服务器)实例自定义 SSL/TLS 配置的方法。

【讨论】:

  • 感谢您的回答。但是我的问题是我的应用运行在Tomee plus J2EE服务器下,封装了CXF。而且我不知道如何在 Tomee 服务器中配置 CXF。我找到的唯一配置文件是一个 tomee.xml 文件和一个 cxf.properties 文件。
猜你喜欢
  • 2017-12-23
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2018-08-03
  • 2014-01-09
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
相关资源
最近更新 更多