2012-05-03 11 views
16

私たちのシステムには、自己署名証明書を鍵ストアに自動的に生成するためのコードがいくつかあり、それがJettyによって使用されます。与えられたホストのためのキーがすでに存在する場合は何も起こりませんが、それが存在しない場合、我々はこのように、新しいキーを生成します。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); 
} 

唯一の鍵と証明書があるので、これはすべてが限り正常に動作しますキーストアであなたが複数のキーを持っていたら、あなたが接続しようとすると、奇妙なことが起こる:

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

これは非常に神秘エラーだったが、私は最終的にサーバに接続していることを主張するユニットテストを書くことで、それを追跡するために管理します証明書のCNがホスト名と一致しています。私が見つけたのはかなり面白かったです - 桟橋はクライアントに提示する証明書を任意に選択するようですが、一貫しています。例えば

は: "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によって何らかの形で決定されます。

答えて

13

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

キーストアには、異なるエイリアスで格納されたいくつかの証明書を持つことができます。エイリアスがcertAlias in the Jetty configurationで明示的に設定されていない場合、SunX509実装は、選択した暗号スイート(通常はRSAですが、ここではあなたのケースではおそらくDSA)の秘密鍵と正しいタイプのキーがある最初のエイリアスを選択します。 。あなたがSun provider implementationを見るならば、選択論理にそれ以上のものがありますが、一般的な秩序、エイリアス名に本当に頼るべきではありません。

もちろん、Jetty独自のSSLContextに独自のX509KeyManagerを付けてエイリアスを選択することもできます。あなたは、実装する必要があります:

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

は残念ながら、離れてkeyTypeissuersから、あなたが決断を下すために得るすべてはsocketそのものです。最高でも、そこに得られる有用な情報は、ローカルIPアドレスとリモートIPアドレスです。

サーバが同じポート上の複数のIPアドレスをリッスンしている場合を除き、常に同じローカルIPアドレスを取得します。(ここでは明らかに少なくとも127.0.0.1192.168.222.100が2つありますが、あなた自身のテストを除いてlocalhostには本当に関心がないと思われます)サーバー側でSNI(Server Name Indication)をサポートする必要があります。要求されたホスト名に基づいて(それをサポートするクライアントによって)決定を行います。残念ながら、SNI was only introduced in Java 7, but only on the client side

ここで直面するもう1つの問題は、Java clients will complain about IP addresses in the Subject DN's CNです。一部のブラウザはこれを許容しますが、これはHTTPS仕様(RFC 2818)に準拠していません。 IPアドレスは、IPアドレスタイプのサブジェクト代替名エントリでなければなりません。

+0

ええ、私たちはIPアドレスも持っていることを発見しました。その結果、独自のHostnameVerifierを書いて一致するかどうかをチェックし、真を返します。もともとは、常に真を返すHostnameVerifierを持っていましたが、それは明白な理由のために投げ出されなければなりませんでした。既存のユーザーの多くは、IPアドレスを「ホスト名」として構成しており、積極的に攻撃したくはありません。 – Trejkaz