我正在尝试从WS
调用SSL
而不是tomee 1.6 server
,但我得到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文件。只有e cxf.properties文件,几乎是空的。
答案 0 :(得分:1)
首先,Tomcat不参与Web服务的消费 - 事实上,它实际上并不涉及您的应用程序正在进行的任何出站连接。
我知道有两种方法可以实现CXF提供的预期结果,其方式不会影响在同一JVM上运行的任何其他出站SSL连接:
第一种方法更可取,因为第二种方法会信任客户端连接的任何端点。
要实现第一种方法,请创建一个包含您希望信任的证书的密钥库(并且为了更好地衡量,请包含任何中间证书)。然后按照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之类的工具从目标端点提取证书(和中介),并将它们导入新的信任存储区。
最后,我想指出,在您的初始解决方案中,使用JDK API支持的SSL套接字工厂和信任管理器等JVM范围安装的危险。在支持多个应用程序的容器内部运行时,可能会产生危险的后果:您可以破坏其他应用程序的安全配置文件。使用像CXF这样的框架的一个好处是它提供了为每个应用程序客户端(或服务器)实例自定义SSL / TLS配置的方法。