2

簡単なセキュア ソケット サーバー クライアント プログラムがあります。
サーバー証明書用に、keytool を使用してキーストアを作成しました。
クライアントからサーバーに接続しようとすると、次の例外が発生し
ます: サーバー内:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

クライアントで:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)

私の理解が正しければ、これらの例外は、作成した証明書を使用しているという事実が原因で発生します。
私の質問は次のとおりです。
サーバーとクライアントの両方で有効な暗号スイートを設定すると、すべての *_anon* 暗号スイートが問題を解決するはずではありませんか?
つまり、暗号スイートを有効にすると、*_anon_*認証は不要になるため、例外はありません。
これは正しいです?
まだ例外が発生するためです。有効な暗号スイートに、すべての有効な暗号スイートと _anon のものを入れてみました。失敗。anonのものだけを設定しようとしましたが、新しい例外が発生しました:

Exception in thread "main" java.lang.IllegalArgumentException: Name must not be null

anon暗号スイートでこれらの例外が発生する理由を誰かが説明してもらえますか?
注:作成したキーストアを指し、サーバーで使用されている
システム プロパティをクライアントに設定すると、通信は正常に行われます。 プログラムは例外なく動作し、データはクライアントからサーバーに正常に送信されます。 javax.net.ssl.trustStore


更新:
これは、anon 暗号を有効にするために使用するスニペットです (サーバーとクライアントの部分でこれを行いました)。

String[] supported = server.getSupportedCipherSuites();
String[] anonCipherSuitesSupported = new String[supported.length];
int count = 0;

for(int i = 0; i < supported.length; i++)
{
    if(supported[i].indexOf("_anon_") > 0)
    {
        anonCipherSuitesSupported[count++] = supported[i];
    }
}

String[] oldEnabled = server.getEnabledCipherSuites();
String[] newEnabled = new String[oldEnabled.length + count];
System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
System.arraycopy(anonCipherSuitesSupported, 0, newEnabled, oldEnabled.length, count);
server.setEnabledCipherSuites(newEnabled);

スタック トレースはクライアント側にあります。

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.AppOutputStream.write(Unknown Source)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
    at sun.nio.cs.StreamEncoder.flush(Unknown Source)
    at java.io.OutputStreamWriter.flush(Unknown Source)
    at com.client.SSLClient1.main(SSLClient1.java:58)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
    at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
    at sun.security.validator.Validator.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    ... 14 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 20 more

サーバー側では:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source)
    at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source)
    at com.server.SecureOrderTaker.main(SecureOrderTaker.java:92) 

今私が単純に行うと:

server.setEnabledCipherSuites(anonCipherSuitesSupported);

anon暗号スイートのみが有効になるように、次のようになります。

Exception in thread "main" java.lang.IllegalArgumentException: Name must not be null
    at com.sun.net.ssl.internal.ssl.CipherSuite.valueOf(Unknown Source)
    at com.sun.net.ssl.internal.ssl.CipherSuiteList.<init>(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.setEnabledCipherSuites(Unknown Source)
    at com.server.SecureOrderTaker.main(SecureOrderTaker.java:82)

ありがとうございました

4

2 に答える 2

5

そうです、*_anon_*暗号は完全な非認証接続に使用されます (サーバーとクライアントの両方が匿名です)。これらの暗号スイートでは、証明書は必要ありません。テストする小さなコードを書きました:

ServerSocketFactory sf = SSLServerSocketFactory.getDefault();
final SSLServerSocket socket = (SSLServerSocket)sf.createServerSocket(443);
System.out.println(Arrays.toString(socket.getSupportedCipherSuites()));
System.out.println(Arrays.toString(socket.getEnabledCipherSuites()));

socket.setEnabledCipherSuites(new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"});

Thread t = new Thread() {
    public void run() {
        try {
            Socket client = socket.accept();
            client.getOutputStream().write("Hello World\n".getBytes("ASCII"));
            client.close();
        } catch (IOException ioe) {
        }
    }
};

t.start();
Thread.sleep(2000);
SSLSocket client = (SSLSocket) SSLSocketFactory.getDefault().createSocket("localhost", 443);
client.setEnabledCipherSuites(new String[] {"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"});
InputStream in = client.getInputStream();
byte[] data = new byte[1024];
int len = in.read(data);
System.out.println(new String(data, 0, len));

このコードが完全ではないことはわかっていますが、クライアントとサーバーの間でデータを正常に交換できました。サーバーまたはクライアントのソケットが適切に構成されていない可能性があります。取得した完全なスタック トレースを提供できますか?

中間者攻撃に対して脆弱であるため、これらの暗号は非推奨であることに注意してください。

更新:問題が見つかりました。配列のanonCipherSuitesSupported長さが長すぎます。したがって*_anon_*、配列を追加した後、一連のnull値で終了します。nullまた、実装は有効な暗号リストで受け入れられないようです。

String[] supported = server.getSupportedCipherSuites();
List<String> list= new ArrayList<String>();

for(int i = 0; i < supported.length; i++)
{
    if(supported[i].indexOf("_anon_") > 0)
    {
        list.add(supported[i]);
    }
}
String[] anonCipherSuitesSupported = list.toArray(new String[0]);
于 2011-02-06T19:13:48.643 に答える
0

あなたは自分で作成した証明書を扱っています。つまり、あなたが認証局です。

問題は、双方が握手して通信するためには、お互いの証明書を信頼する必要があることです。信頼は、証明書から CA までのチェーンを構築して、特定の側がその CA を信頼しているかどうかを確認することによって確立されます (証明書が取り消されていないかどうかを確認するなどの追加の手順も必要になる場合があります)。Java では、ほとんどの主要な CA がデフォルトで信頼されています。あなたの場合、カスタム CA はそうではありません。

つまり、サーバー証明書はクライアントのトラストストアにある必要があります(前述のように、それをポイントすると問題が解決します)。それ、または CA の証明書 (より適切な選択)。

SSL/TLS の一般的な詳細については、こちらをご覧ください。

相互信頼を扱っている場合、サーバーはクライアント証明書を発行した CA も信頼する必要があります。

于 2011-02-06T19:27:45.607 に答える