【问题标题】:Apache HttpClient able to communicate over HTTPS when DIRECT but not via PROXY error: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticatedApache HttpClient 能够在 DIRECT 时通过 HTTPS 进行通信,但不能通过 PROXY 错误:javax.net.ssl.SSLPeerUnverifiedException:peer not authenticated
【发布时间】:2011-12-20 18:54:21
【问题描述】:

我已经阅读了许多不同的示例,但我目前在尝试使用 HTTPS 通过代理进行通信时遇到了困难。我有一个包装器来创建一个 Apache HttpClient,如下面的代码所示。

目前,如果我在没有设置代理的情况下拨打电话,它将使用我来自 SSLSocketFactory 的信任库并正确允许通过 SSL 进行通信。唯一需要的证书是不需要身份验证的威瑞信服务器证书。

当我设置代理时,我收到一条错误消息:

javax.net.ssl.SSLPeerUnverifiedException:对等体未通过身份验证

我觉得我一定是缺少某种类型的代理设置,它使代理连接使用相同的 SSLSocketFactory?

我使用 -Djavax.net.debug=ssl 进行了测试,直接访问时我可以看到更多的 SSL 活动。当我使用直接时,我可以看到所有随请求加载和发送的密钥,当我使用代理时,我只看到:

httpConnector.receiver.3, setSoTimeout(30000) called
%% No cached client session
*** ClientHello, TLSv1
RandomCookie:  GMT: 1307565311 bytes = { 184, 216, 5, 151, 154, 212, 232, 96, 69, 73, 240, 54, 236, 26, 8, 45, 109, 9, 192,
227, 193, 58, 129, 212, 57, 249, 205, 56 }
Session ID:  {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_C
BC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH
_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH
_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
]
Compression Methods:  { 0 }
***
httpConnector.receiver.3, WRITE: TLSv1 Handshake, length = 73
httpConnector.receiver.3, WRITE: SSLv2 client hello message, length = 98
httpConnector.receiver.3, handling exception: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
httpConnector.receiver.3, SEND TLSv1 ALERT:  fatal, description = unexpected_message
httpConnector.receiver.3, WRITE: TLSv1 Alert, length = 2
httpConnector.receiver.3, called closeSocket()
httpConnector.receiver.3, IOException in getSession():  javax.net.ssl.SSLException: Unrecognized SSL message, plaintext conn
ection?
httpConnector.receiver.3, called close()
httpConnector.receiver.3, called closeInternal(true)
httpConnector.receiver.3, called close()
httpConnector.receiver.3, called closeInternal(true)
2011-12-20 11:11:59,401 [httpConnector.receiver.3] INFO - The JavaScript method AddEvent threw an exception of type class co
m.alarmpoint.integrationagent.soap.exception.SOAPRequestException with message "javax.net.ssl.SSLPeerUnverifiedException: pe
er not authenticated".  The exception will be propogated up the call stack.

谁能帮帮我。这是我设置代理和 SSLSocketFactory 的代码。

var client = httpClientWrapper.getHttpClient();
var proxy = new HttpHost(PROXY_HOST, PROXY_PORT, "https"); 
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); 

var authpref = new ArrayList();
authpref.add(AuthPolicy.BASIC);

client.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);

ServiceAPI.getLogger().debug("KeyStore.getDefaultType() " + KeyStore.getDefaultType());


var trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());        
var instream = new FileInputStream(new File("conf/my.truststore")); 
try {
ServiceAPI.getLogger().debug("getting trustore");
trustStore.load(instream, "changeit".split(''));
} finally {
instream.close();
}


var socketFactory = new SSLSocketFactory(trustStore);
var sch = new Scheme("https", socketFactory, 443);

client.getConnectionManager().getSchemeRegistry().register(sch);

堆栈跟踪:

Caused by: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:390)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:488)
at org.apache.http.conn.scheme.SchemeSocketFactoryAdaptor.connectSocket(SchemeSocketFactoryAdaptor.java:62)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148)
at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:561)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)

【问题讨论】:

    标签: apache authentication https proxy httpclient


    【解决方案1】:

    这是 aaron 解决方案的一个变体,使用 Java(与 Groovy 相比)。该解决方案还避免了 HttpClientWrapper 类(它从何而来?),并直接加载代理的证书。它是针对 HttpClient 4.2 编写的(但我认为它应该适用于 4.0)。作为额外的奖励,它包括一个 Windows 代理(例如 Microsoft ForeFront TMG)的代理身份验证示例。

    我花了足够长的时间拼凑起来,我想我应该分享它:

        HttpParams params = new BasicHttpParams();
        DefaultHttpClient.setDefaultHttpParams( params );   // Add the default parameters to the parameter set we're building
        DefaultHttpClient client = new DefaultHttpClient( params );
    
        KeyStore trustStore = KeyStore.getInstance( KeyStore.getDefaultType() );
        trustStore.load( null );
    
        InputStream certStream = new FileInputStream( "cert-file" );
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate)cf.generateCertificate(certStream);
        certStream.close();
        trustStore.setCertificateEntry( "proxy-cert", cert );
    
        SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
        client.getConnectionManager().getSchemeRegistry().register( new Scheme( "https", 443, socketFactory ));
    
        client.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, 
                               new HttpHost( "my-proxy", 8080 ));
    
        // These 3 lines are only needed if your proxy is Windows based & requires authentication
        AuthScope scope = new AuthScope( "myproxy", 8080, null, AuthPolicy.NTLM );
        Credentials credentials = new NTCredentials( "username", "changeit", "WORKSTATION", "MY-DOMAIN" );
        client.getCredentialsProvider().setCredentials( scope, credentials );
    
        HttpGet get = new HttpGet( "https://mysite.com/resource" );
        String result = client.execute( get, new BasicResponseHandler() );
    
        System.out.println( result );
    

    【讨论】:

      【解决方案2】:

      我解决了这个问题。我在调试 HttpClients 代码后发现的问题是我的代理配置方式和方案可用。

      HttpRoute[{tls}->https://someproxy->https://some_endpoint:443]
      

      问题是代理是为 https 方案设置的,但它实际上是在 http 上运行的。当包装器没有配置 http 方案时,这成为一个问题。最后,我为我的信任库和默认的 http 方案创建了 SSLSocketFactory,并正确设置了我的代理。

      // Setup the Keystore and Schemes for the HttpClient and Proxy
      var trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());        
      var instream = new FileInputStream(new File("conf/my.truststore")); 
      try {
          trustStore.load(instream, "changeit".split(''));
      } finally {
          instream.close();
      }
      
      var socketFactory = new SSLSocketFactory(trustStore);
      var schHttp = new Scheme("http", PROXY_PORT, PlainSocketFactory.getSocketFactory());
      
      // Create the HttpClient wrapper which will have the truststore SSLSocketFactory and a default http scheme and proxy setup 
      httpClientWrapper = new HttpClientWrapper("some_endpoint", 443, "/", socketFactory);
      var client = httpClientWrapper.getHttpClient();
      var proxy = new HttpHost(PROXY_HOST, PROXY_PORT, "http"); 
      client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
      
      client.getConnectionManager().getSchemeRegistry().register(schHttp);
      

      【讨论】:

        【解决方案3】:

        您是否尝试在启动 java 进程时使用全局 -Dhttp.proxyHost=proxy.host.com -Dhttp.proxyPort=8080 来验证 SSLSocketFactory 没有回退到无代理通信。

        【讨论】:

        • 是的,但在通过代理通信时不使用这些。如果我设置了这些,我可以验证当我设置这些并使用 ProxySelector.getDefault().select("myurl") 它返回要使用的代理。问题是 Apache HTTPClient 没有使用它。我验证了这一点,因为如果我在 JVM 中设置它并将 TCPmon 设置为代理,我看不到这一点。我在 Apache 的 HTTPClient 4.1.1 中找到的正确使用代理的唯一方法是通过 var proxy = new HttpHost(PROXY_HOST, PROXY_PORT, "https"); client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-05
        • 2023-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多