HttpsUrlConnection(通过Jersey客户端调用)不调用set SSLSocketFactory

时间:2015-06-29 23:42:47

标签: java ssl https jersey jax-rs

请查看此问题的底部以获取更多最新信息

我试图通过我的Jersey客户端拦截所有SSL握手(以便我可以获取有关它们的信息以及向用户呈现视觉信息,就像浏览器中的绿色锁定一样)。不幸的是,看起来Jersey似乎没有使用我的SSLSocketFactory实现,因为没有调用任何createSocket方法。没有错误发生,只是没有任何记录。代码应该清楚:

调用+实例化:

this.httpClient = getHttpsClient(new DefaultSSLContextProvider());
Invocation.Builder invBuilder = httpClient.target(API_URL_PRIVATE + API_VERSION_2 + "markets").request(MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN, MediaType.TEXT_HTML);
invBuilder.header("Content-Type", "application/x-www-form-urlencoded");
invBuilder.header("User-Agent", USER_AGENT);

Response response = invBuilder.get();
logger.debug("response: " + response);

的HttpClient:

public Client getHttpsClient(SSLContextProvider sslContextProvider) throws KeyStoreException
{
    ClientConfig config = new ClientConfig().connectorProvider(new HttpUrlConnectorProvider().connectionFactory(
            url ->
            {
                HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
                connection.setSSLSocketFactory(sslContextProvider.getSSLSocketFactory());
                return connection;
            }));

    return ClientBuilder.newBuilder()
            .sslContext(sslContextProvider.getSSLContext())
            .withConfig(config)
            .build();
}

DefaultSSLContextProvider:

public class DefaultSSLContextProvider implements SSLContextProvider
{
    private SSLContext sslContext;
    private ObservableSSLSocketFactory observableSSLSocketFactory;

    private static final Logger logger = LoggerFactory.getLogger(DefaultSSLContextProvider.class);

    public DefaultSSLContextProvider()
    {
        try
        {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
            sslContext = SSLContext.getInstance("SSL");
            KeyStore keyStore = getKeyStore();
            trustManagerFactory.init(keyStore);
            sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
            observableSSLSocketFactory = new ObservableSSLSocketFactory(sslContext);
            HttpsURLConnection.setDefaultSSLSocketFactory(observableSSLSocketFactory);
            SSLContext.setDefault(sslContext);
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new RuntimeException(e);
        }
        catch (KeyManagementException | KeyStoreException e)
        {
            logger.error("could not create DefaultSSLContextProvider", e);
            throw new IllegalStateException(e);
        }
    }

    @Override
    public SSLContext getSSLContext()
    {
        return sslContext;
    }

    @Override
    public SSLSocketFactory getSSLSocketFactory()
    {
        return observableSSLSocketFactory;
    }

    @Override
    public KeyStore getKeyStore()
    {
        // snip
    }
}

ObservableSSLSocketFactory:

/**
 * Based heavily on:
 * http://stackoverflow.com/a/23365536/3634630
 */
public class ObservableSSLSocketFactory extends SSLSocketFactory
{
    private final SSLContext sslContext;
    private final String[] preferredCipherSuites;
    private final String[] preferredProtocols;

    private static final Logger logger = LoggerFactory.getLogger(ObservableSSLSocketFactory.class);

    protected ObservableSSLSocketFactory(SSLContext sslContext)
    {
        logger.debug("CREATING OBSERVABLE SOCKET FACTORY!");
        this.sslContext = sslContext;
        preferredCipherSuites = getCiphers();
        preferredProtocols = getProtocols();
        logger.debug("Observable socket factory created");
        logger.debug("preferredCipherSuites: " + preferredCipherSuites);
        logger.debug("preferredProcotols: " + preferredProtocols);
    }

    @Override
    public String[] getDefaultCipherSuites()
    {
        return preferredCipherSuites;
    }

    @Override
    public String[] getSupportedCipherSuites()
    {
        return preferredCipherSuites;
    }

    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException
    {
        logger.debug("creating ssl socket");
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(s, host, port, autoClose);

        sslSocket.addHandshakeCompletedListener(new HandshakeListener());
        sslSocket.setEnabledProtocols(preferredProtocols);
        sslSocket.setEnabledCipherSuites(preferredCipherSuites);

        return sslSocket;
    }

    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
    {
        logger.debug("creating ssl socket");
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(address, port, localAddress, localPort);

        sslSocket.addHandshakeCompletedListener(new HandshakeListener());
        sslSocket.setEnabledProtocols(preferredProtocols);
        sslSocket.setEnabledCipherSuites(preferredCipherSuites);

        return sslSocket;
    }

    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException
    {
        logger.debug("creating ssl socket");
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(host, port, localHost, localPort);

        sslSocket.addHandshakeCompletedListener(new HandshakeListener());
        sslSocket.setEnabledProtocols(preferredProtocols);
        sslSocket.setEnabledCipherSuites(preferredCipherSuites);

        return sslSocket;
    }

    public Socket createSocket(InetAddress host, int port) throws IOException
    {
        logger.debug("creating ssl socket");
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(host, port);

        sslSocket.addHandshakeCompletedListener(new HandshakeListener());
        sslSocket.setEnabledProtocols(preferredProtocols);
        sslSocket.setEnabledCipherSuites(preferredCipherSuites);

        return sslSocket;
    }

    public Socket createSocket(String host, int port) throws IOException
    {
        logger.debug("creating ssl socket");
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(host, port);

        sslSocket.addHandshakeCompletedListener(new HandshakeListener());
        sslSocket.setEnabledProtocols(preferredProtocols);
        sslSocket.setEnabledCipherSuites(preferredCipherSuites);

        return sslSocket;
    }

    private String[] getProtocols()
    {
        // snip
    }

    private String[] getCiphers()
    {
        // snip
    }

    class HandshakeListener implements HandshakeCompletedListener
    {
        public HandshakeListener()
        {
            logger.debug("Created new HandshakeListener");
        }

        public void handshakeCompleted(HandshakeCompletedEvent e)
        {
            logger.debug("Handshake successful!");
            logger.debug("using cipher suite: " + e.getCipherSuite());
        }
    }

}

正如我所说,没有异常或错误发生(事实上原始请求没有问题(HTTP 200),但是记录的唯一内容是:

00:01:37.867 DEBUG [ObservableSSLSocketFactory] CREATING OBSERVABLE SOCKET FACTORY!
00:01:38.072 DEBUG [ObservableSSLSocketFactory] Observable socket factory created
00:01:38.073 DEBUG [ObservableSSLSocketFactory] preferredCipherSuites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
00:01:38.073 DEBUG [ObservableSSLSocketFactory] preferredProcotols: [TLSv1, TLSv1.1, TLSv1.2]
00:01:39.435 DEBUG [Exchange] response: InboundJaxrsResponse{context=ClientResponse{method=GET, uri=https://www.bitstamp.net/api/order_book/, status=200, reason=OK}}

来自createSocket()的任何内容或HandshakeCompletedListener。

非常感谢任何帮助。

更新1: 我添加了一些额外的日志声明,情况确实很奇怪。 Jersey客户端实际上正在调用HttpUrlConnectorProvider实现,实际上在连接上设置了ObservableSSLSocketFactory的实例,看来当在HttpsURLConnection上调用connect方法时,它不会使用套接字工厂。

更新2: 我发现了old bug标题为:" HttpsURLConnection没有使用set SSLSocketFactory来创建它的所有套接字"。这似乎是我的确切问题。据说这个bug在JDK7中有所修复。我正在使用(如上所述)8u60,这已经超出了这个bug被修复的时间。我很困惑。我发现java参数-Djavax.net.debug = all并设置它 - 我没有看到任何错误或任何不合适的地方,似乎HttpsUrlConnection没有使用它上面的SSLSocketFactory设置。

更新3: 在#java @ irc.freenode.net上接受了wyvern的建议,我决定使用apache-connector而不是JDK HttpsUrlConnection连接器,最后经过一些涂抹和哄骗后,我看到了:

[HandshakeCompletedNotify-Thread][ObservableSSLConnectionSocketFactory] Handshake successful!

所以,我想我已经准备好了然后=)。

1 个答案:

答案 0 :(得分:-1)

在进行SSL握手之前,请确保客户端和服务在服务器(客户端和服务)上都安装了正确的证书。有关SSLFactory实现的信息,请参阅网址:http://www.programcreek.com/java-api-examples/index.php?api=javax.net.ssl.SSLSocketFactory

相关问题