2013-04-04 89 views
3

我遇到這個大問題,我在google上找不到任何答案,所以我試着寫在這裏。Apache HttpClient 4.2.3 - SSLException:證書中的主機名不匹配

我正在開發一個Web服務客戶端。要連接到Web服務,我必須創建一個帶有客戶端身份驗證的ssl通道並使用代理(該呼叫必須從白名單IP完成)。爲此,我在WebSphere 6.1環境下使用帶Java 1.5的HttpClient 4.2.3庫(與1.4兼容的模式)。

作爲工作有關,我刪除到實際的Web服務我打電話,所以你會發現這裏的一些奇怪的變量/主機/類名稱相關的內容有:)

我已經設置了這樣

// Costruzione del proxy 
    Resources res = new Resources(); 
    String proxyHost = res.get(PROXY_HOST); 
    int proxyPort = Integer.parseInt(res.get(PROXY_PORT)); 

    HttpHost proxy = new HttpHost(proxyHost, proxyPort); 


    // Costruzione del post method 
    String serviceUrl = "https://" + someHost + meth.getServiceURL(); 
    HttpPost post = new HttpPost(serviceUrl); 
    post.addHeader("Content-Type", "text/xml"); 

    String soapAction = "https://" + someHost + meth.getAction(); 
    post.addHeader("SOAPAction", soapAction); 

    AbstractHttpEntity postBody = new StringEntity(soapEnvelope); 
    post.setEntity(postBody); 


    // Costruzione dell'ambiente di chiamata HTTPS 
    SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore); 
    Scheme httpsScheme = new Scheme("https", HTTPS_PORT, sslSocketFactory); 

    Scheme httpScheme = new Scheme("http", HTTP_PORT, PlainSocketFactory.getSocketFactory()); 

    final SchemeRegistry schemeRegistry = new SchemeRegistry(); 
    schemeRegistry.register(httpScheme); 
    schemeRegistry.register(httpsScheme); 

    PoolingClientConnectionManager connManager = new PoolingClientConnectionManager(schemeRegistry); 

    HttpParams params = new BasicHttpParams(); 

    HttpClient client = new DefaultHttpClient(connManager, params); 
    client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); 


    // CHIAMATA A MEF 
    HttpResponse resp = client.execute(post); 

密鑰庫和信任的連接已經從一個文件

File ksFile = new File(res.get(KEY_STORE_PATH_CONF)); 
File tsFile = new File(res.get(TRUST_STORE_PATH_CONF)); 
InputStream ksInput, tsInput; 

try { 
    ksInput = new FileInputStream(ksFile); 
    tsInput = new FileInputStream(tsFile); 
} catch ... {} 

KeyStore keyStore, trustStore; 
try { 
    String ksPassword = res.get(KEY_STORE_PASS_CONF); 
    String tsPassword = res.get(TRUST_STORE_PASS_CONF); 

    keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
    keyStore.load(ksInput, ksPassword.toCharArray()); 

    trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
    trustStore.load(tsInput, tsPassword.toCharArray()); 
} catch ... {} 

當我嘗試調用client.execute方法加載,它給了我這個異常

javax.net.ssl.SSLException: hostname in certificate didn't match: <right.host.it> != <unknown.host.it> 
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java) 
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java) 
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java) 
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java) 
at org.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:628) 
at org.apache.http.impl.conn.DefaultClientConnectionOperator.updateSecureConnection(DefaultClientConnectionOperator.java:232) 
at org.apache.http.impl.conn.ManagedClientConnectionImpl.layerProtocol(ManagedClientConnectionImpl.java:401) 
at org.apache.http.impl.client.DefaultRequestDirector.establishRoute(DefaultRequestDirector.java:842) 
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:649) 
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java) 
at my.class.package.MyClass.callWs(MyWebService.java) 

有一些,我注意到奇怪的事情:

  • 異常消息未知的主機是正確的的同一個域,所以它是從公司的證書有必要採取
  • 我試圖調試調用,它看起來像需要其他證書。 unknown.host.it地址取自x509證書的CN(在調試中看到),並且密鑰庫文件中的兩個證書都包含其他CN。此外,我的密鑰庫中有兩個證書,當調用第一個AbstractVerifier.verify方法時,在調用參數中有三個證書,其中一個證書實際上包含「unknown.host.it」字符串,找不到任何證書在unknown.host.it串在我擁有

這個問題可能是,我不知道怎麼SSL實際工作,所以如果你看到任何完全錯誤的東西我說這可能解決我的問題的關鍵。

你能以某種方式幫助我嗎? 非常感謝!

更新1:後:) 因爲我想先收到回覆(本公司擁有對軟件進行測試)的日子,我已經決定要強制軟件接受每一個主機名。我想稍後改變它,但現在我只想讓它工作。所以我加入這個(不建議使用:))線,以我的代碼

SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore); 
    sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 

現在它把主機名驗證(意外驚喜:)),但仍然給了我一個403 Forbidden錯誤。該服務的擁有者說我從一個白名單的IP呼叫,所以問題在於,我想,服務器不認識我。他們說我沒有出示任何證書。

我的方法來添加證書是在SSLSocketFactory構造函數,我通過密鑰庫與我的證書裏面。我是否需要指定HttpClient,以便在握手時自我識別服務器?

謝謝:)

+0

我通過激活可信主機名驗證程序(即SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)找到了一個臨時解決方案。我知道這不是正確的解決方案,但我有時間解決這個問題。 問題是HTTP一直給我返回一個403-禁止的錯誤。我打電話給服務支持,他們說我打電話從白名單IP(所以代理工作),但相互身份驗證不起作用的原因。 – CodingMonkey 2013-04-05 08:41:07

+0

相關:http://stackoverflow.com/q/7256955/435605 – 2016-07-06 16:32:43

回答

3

這可能是因爲您的服務器使用的服務器名稱指示(SNI)對同一IP地址(和端口)有兩個主機名。

您可以使用openssl,通過顯式指定服務器名稱來檢查。例如,這兩個命令將連接到相同的IP地址,但要求不同的主機名和服務於不同的證書:

openssl s_client -connect www.google.co.uk:443 -servername www.google.co.uk 

openssl s_client -connect www.google.co.uk:443 -servername www.google.com 

的Java只支持目前客戶端SNI,只有Java的,因爲7

+0

我使用Java 5 :) 但它實際上曾經工作過一次...它只是停止這樣做,我仍然想知道爲什麼.. – CodingMonkey 2013-04-05 08:53:53

+0

你應該真的升級。也許在服務器上發生了變化? – Bruno 2013-04-05 10:15:32

+0

我同意,但是...我不是那個決定的人。作爲所有大公司,我的*有點*不可能升級技術:) – CodingMonkey 2013-04-05 13:48:02

相關問題