19

私たちのシステムには、Jetty によって使用されるキー ストアに自己署名証明書を自動的に生成するためのコードがいくつかあります。特定のホストのキーが既に存在する場合は何も起こりませんが、存在しない場合は、次のように新しいキーを生成します。

public void generateKey(String commonName) {
    X500Name x500Name = new X500Name("CN=" + commonName);
    CertAndKeyGen keyPair = new CertAndKeyGen("DSA", "SHA1withDSA");
    keyPair.generate(1024);
    PrivateKey privateKey = keyPair.getPrivateKey();
    X509Certificate certificate = keyPair.getSelfCertificate(x500Name, 20*365*24*60*60);
    Certificate[] chain = { certificate };
    keyStore.setEntry(commonName, privateKey, "secret".toCharArray(), chain);
}

キー ストアにキーと証明書が 1 つしかない限り、これはすべて正常に機能します。複数のキーを取得すると、接続しようとすると奇妙なことが起こります。

java.io.IOException: HTTPS hostname wrong:  should be <127.0.0.1>

これは非常に不可解なエラーでしたが、サーバーに接続し、証明書の CN がホスト名と一致することをアサートする単体テストを作成することで、最終的に追跡することができました。私が発見したことは非常に興味深いものでした。Jetty は、クライアントに提示する証明書を恣意的に選択しているようですが、一貫した方法で行われているようです。

例えば:

  • 「CN=localhost」と「CN=cheese.mydomain」がキー ストアにある場合、常に「CN=cheese.mydomain」が選択されます。
  • 「CN=127.0.0.1」と「CN=cheese.mydomain」がキー ストアにある場合、常に「CN=cheese.mydomain」が選択されます。
  • "CN=192.168.222.100" (cheese.mydomain) と "CN=cheese.mydomain" がキー ストアにある場合、常に "CN=192.168.222.100" が選択されます。

ストア内の証明書をループして印刷するコードをいくつか書きましたが、最初の証明書またはそのような些細なことを一貫して選択していないことがわかりました。

では、正確にはどのような基準を使用しているのでしょうか。最初は localhost は特別だと思っていましたが、3 番目の例に完全に戸惑いました。

これは、私の場合は SunX509 である KeyManagerFactory によって何らかの形で決定されると思います。

4

1 に答える 1

16

これは実際、KeyManager(通常は から取得されるKeyManagerFactory) によって最終的に決定されます。

キーストアには、さまざまな別名で格納された多数の証明書を含めることができます。certAliasJetty構成でエイリアスが明示的に構成されていない場合、SunX509実装は、選択した暗号スイート(通常はRSAですが、おそらくDSA)に適したタイプの秘密鍵と鍵がある最初のエイリアスを選択しますここ)。Sun プロバイダーの実装 を見ると、選択ロジックにはもう少しありますが、一般的には順序に依存するべきではなく、エイリアス名だけに依存する必要があります。

もちろん、Jetty に独自のSSLContextものを指定してX509KeyManager、エイリアスを選択することもできます。以下を実装する必要があります。

 chooseServerAlias(String keyType, Principal[] issuers, Socket socket)

残念ながら、 と を除いてkeyTypeissuers決定を下せるのはsocket自体だけです。せいぜい、そこで得られる有用な情報は、ローカル IP アドレスとリモート IP アドレスです。

サーバーが同じポートで複数の IP アドレスをリッスンしていない限り、常に同じローカル IP アドレスを取得します。(ここでは、明らかに、少なくとも 2 つの :127.0.0.1192.168.222.100がありますが、独自のテストを除いて、localhost にはあまり関心がないのではないかと思います。) サーバー側でサーバー名表示 (SNI) をサポートして、要求されたホスト名に基づく決定 (それをサポートするクライアントによる)。残念ながら、SNI は Java 7 でのみ導入されましたが、クライアント側でのみ導入されました

ここで直面するもう 1 つの問題は、Java クライアントがサブジェクト DN の CN の IP アドレスについて文句を言うことです。一部のブラウザーはこれを許容しますが、これは HTTPS 仕様 (RFC 2818) に準拠していません。IP アドレスは、IP アドレス タイプの Subject Alternative Name エントリである必要があります。

于 2012-05-03T08:53:41.277 に答える