2011-12-20 118 views
1

雖然我已經閱讀了許多不同的示例,但我目前有困難嘗試通過代理使用HTTPS進行通信。我有一個包裝來創建一個Apache HttpClient,如下面的代碼所示。Apache HttpClient能夠通過HTTPS進行通信時DIRECT但不通過PROXY錯誤:javax.net.ssl.SSLPeerUnverifiedException:對端未驗證

當前,如果我在未設置代理的情況下撥打電話,它將使用SSLSocketFactory中的我的信任庫,並正確地允許通過SSL進行通信。所需的唯一證書是不需要驗證的verisign服務器證書。

當我設置一個代理我得到一個錯誤說:

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) 

回答

3

以下是關於aaron解決方案的一個變體,用Java(vs 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); 
0

您是否嘗試過使用全球-Dhttp.proxyHost = proxy.host.com -Dhttp.proxyPort = 8080啓動你的java進程時,驗證SSLSocketFactory的ISN不會回到無代理通信。

+0

是的,但是通過代理進行通信時,它不使用這些。如果我設置這些,我可以驗證,當我設置這些並使用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); – ALM 2011-12-21 11:13:02

1

我解決了這個問題。我發現一旦調試到HttpClient代碼的問題是我的代理被配置和方案可用的方式。

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); 
相關問題