5

TLS で JSSE を使用する Java で。サーバーとクライアントの間に安全なソケットを作成しました。最終的にソケットを安全に接続できるようになった後でも、既存のコードのセキュリティについて根本的な疑問が残ります。私はチュートリアルの指示に従いました。JavaDoc のドキュメントは非常に正確ですが、専門用語のスワヘリ方言を話さない限り、少しあいまいです....

私はかなり長い間 C++ でネットワーク プログラミングを行ってきました。Java への移行は簡単でした。しかし、最近では、トラフィックを安全にすることが賢明であることがわかりました。これは言われています:

Web ブラウザがセキュア ソケットを作成するのと同じ方法でセキュア ソケットを作成したいので、双方向のトラフィックが暗号化されます。クライアントは、サーバーから送信された個人アカウント情報を見ることができ (傍受された場合は非常に悪い)、クライアントは自分のユーザー名とパスワードをサーバーに安全に送信できます (傍受された場合も非常に悪い)。

公開鍵暗号がどのように機能するかについてはすべて知っていますが、公開鍵暗号だけでは副作用があります。公開鍵をクライアントに送信すると、クライアントは公開鍵で暗号化し、サーバーのみが復号化できるデータをサーバーに送信します。私が理解していることから、サーバーは秘密鍵を使用してクライアントに送信されるメッセージを暗号化し、公開鍵を持っている人がそれを復号化できないようにするには、別のセキュリティ層を追加する必要があります。

  1. ファイルpublic.keyとprivate.keyに保存された公開/秘密鍵のペアがあります(これらはJSSEのkeytoolユーティリティを使用して作成しました
  2. クライアントに public.key を含めました
  3. サーバーにprivate.keyを含めました

クライアントクラス:

    KeyStore keyStore;
    TrustManagerFactory tmf;
    KeyManagerFactory kmf;
    SSLContext sslContext;
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.nextInt();

        keyStore = KeyStore.getInstance("JKS");
        keyStore.load(this.getClass().getClassLoader().getResourceAsStream("server.public"),"public".toCharArray());
        tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(keyStore);
        kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keyStore, "public".toCharArray());
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), secureRandom);
        SSLSocketFactory sslsocketfactory = sslContext.getSocketFactory();
        SSLSocket sslsocket = (SSLSocket)sslsocketfactory.createSocket("localhost", 9999);

サーバークラス:

    String passphrase = "secret"
    KeyStore keyStore;
    TrustManagerFactory tmf;
    KeyManagerFactory kmf;
    SSLContext sslContext;
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.nextInt();

        keyStore = KeyStore.getInstance("JKS");
        keyStore.load(this.getClass().getClassLoader().getResourceAsStream("server.private"),passphrase.toCharArray());
        tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(keyStore);
        kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(keyStore, passphrase.toCharArray());
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), secureRandom);
        SSLServerSocketFactory sslserversocketfactory = sslContext.getServerSocketFactory();
        SSLServerSocket sslserversocket =
        (SSLServerSocket)sslserversocketfactory.createServerSocket(9999);

/ * ** * ** *質問* ** * ** * * /

すべてが機能します!私はソケットを BufferedReader と BufferedWriter に接続し、accept(); の後で美しくやりとりを始めます。クライアントからの接続を開始し、クライアントとサーバーの送受信ループを開始します。

これで、この時点でクライアントからサーバーへの通信が安全であることがわかりました。クライアントからのトラフィックを復号化できるのは、サーバー キーだけです。しかし、サーバーからクライアントへの通信はどうでしょうか? クライアントの鍵はサーバーからのメッセージを復号化できますが、Public Key Crypto 101 では、クライアントが公開鍵をサーバーに送信することになっていることがわかります。これは、このコードの舞台裏で起こっていますか? SSLContext はこれを処理しましたか? または、クライアントからサーバーへの暗号化された接続が確立されたので、クライアントの秘密鍵と公開鍵のペアも生成する必要がありますか?

上記のコードで送受信されるトラフィックが実際に両方向で安全かどうか教えてください。

4

2 に答える 2

6

SSL / TLSの証明書(およびそれらの秘密鍵)は、SSL / TLSの関係者を認証するためにのみ使用されます(多くの場合、サーバーのみが証明書を使用します)。

実際の暗号化は、ハンドシェイク中にネゴシエートされる共有/対称鍵を使用して行われます。これは、認証された鍵交換の形式を使用して交換されるプレマスター鍵から派生します(TLS仕様のセクションF.1.1を参照) 。

この認証された鍵交換がどのように行われるかは暗号スイートによって異なりますが、最終的な結果は同じです。つまり、証明書の秘密鍵を持つクライアントとサーバーだけが知っていることが保証された、2者間で共有されるプレマスターシークレットです。 。

そのプレマスターシークレット交換に続いて、マスターシークレット自体が計算され、そこからシークレットキーのペアが導出されます(キーの計算のセクションで説明)。1つはクライアントが書き込む(およびサーバーが読み取る)ためのもので、もう1つはサーバーが書き込むため(およびクライアントが読み取るため)。(接続の整合性を保証するために、MACシークレットも生成されます。)

原則として、すべての暗号スイートが暗号化と認証されたキー交換を提供するわけではありませんが(暗号スイートの定義のセクションを参照)、SunJSSEプロバイダーを使用するJSSEでデフォルトで有効になっているすべての暗号スイートが提供します(SunJSSEプロバイダーのドキュメントの暗号スイートの表を参照)。anonつまり、名前が含まれている、または名前が含まれている暗号スイートを有効にしないでくださいNULL

あなたのコードに関して:

  • このようなKey/TrustManagerFactoryアルゴリズム(「SunX509」)を修正するコードの例は複数あります。これは通常、Java1.4のデフォルトをハードコーディングするコードです。Java 5以降、デフォルトのTMFアルゴリズムはですPKIXJSSEリファレンスガイドの「カスタマイズ」セクションを参照)。これを回避する最善の方法は、TrustManagerFactory.getDefaultAlgorithm()(KMFでも同じ)を使用することです。これにより、サポートされていない他のJRE SunX509(IBMなど)でもコードを実行できるようになります。

  • クライアント証明書認証を使用していないためKeyManagerFactory、クライアント側でを使用しても意味がありません。とにかく秘密鍵を持っていない可能性のあるキーストアで初期化すると、意味がなくなります。を使用した方がよいでしょうsslContext.init(null, tmf.getTrustManagers(), null)。(どちらの場合も、セキュアランダムについても同じです。JSSEにデフォルト値を使用させます。)

于 2012-10-15T08:42:32.680 に答える
1

PKI がどのように機能するかは理解していますが、SSL 実装の 2 つの重要な部分が欠けています。まず、ほとんどの PKI アルゴリズムでは、トラフィックを双方向で暗号化できます。公開鍵を使用して暗号化メッセージを送信でき、秘密鍵を持っている人だけがそれを読むことができます。これを暗号化と呼びます。秘密鍵を使用してメッセージを暗号化することもでき、公開鍵を持っている人は誰でもそれを復号化できます。これはデジタル署名と呼ばれます。

もう 1 つの欠けている部分は、SSL がクライアントとサーバー間のネットワーク トラフィックの送信に PKI を使用しないことです。対称暗号化アルゴリズムを使用します。ただし、対称暗号化のキー (セッション キーと呼ばれる) は、PKI と証明書を使用するかなり複雑なチャレンジ/レスポンス プロトコルを使用して確立されます。このフェーズでは、サーバーは中間者ではないことをクライアントに証明します。クライアントは、より強力な認証のための証明書がある場合は、オプションでそれをサーバーに証明し、対称セッション キーが確立されます。詳細はこちらhttps://www.rfc-editor.org/rfc/rfc5246

対称キーは、RC5 や AES などのアルゴリズムを使用してトラフィックを暗号化するために使用されます

于 2012-10-15T03:28:37.937 に答える