3

私はこの大きな問題を抱えており、グーグルで答えが見つからないので、ここに書き込もうとしています。

私はWebサービスクライアントを開発しています。Web サービスに接続するには、クライアント認証を使用して ssl チャネルを作成し、プロキシを使用する必要があります (呼び出しは、ホワイトリストに登録された IP から行う必要があります)。これを行うために、WebSphere 6.1 環境で Java 1.5 (1.4 との互換モード) で HttpClient 4.2.3 ライブラリを使用しています。

仕事に関連しているため、呼び出している実際の 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 が含まれています。さらに、キーストアに 2 つの証明書があり、最初の AbstractVerifier.verify メソッドが呼び出されると、呼び出しパラメーターに 3 つの証明書があり、そのうちの 1 つに CN に 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 を指定する必要がありますか?

ありがとう :)

4

1 に答える 1

3

サーバーが Server Name Indication (SNI) を使用して、同じ IP アドレス (およびポート) で 2 つのホスト名を提供している可能性があります。

opensslこれは、サーバー名を明示的に指定することで確認できます。たとえば、次の 2 つのコマンドは同じ 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 以降でのみサポートされています。

于 2013-04-04T15:11:15.330 に答える