- PR -

Tomcat(SSL3)でクライアント証明書を取得

1
投稿者投稿内容
ふうた
大ベテラン
会議室デビュー日: 2001/08/23
投稿数: 198
お住まい・勤務地: 岡山
投稿日時: 2003-02-18 23:04
TomcatでSSL3のクライアント認証を試しています。
Tomcat自体の設定はうまくいって(と思う)、ブラウザでアクセスしたときに
クライアントの証明書を選択するダイアログが表示されるようになりました。

あとは、このクライアント証明書をサーブレットで取得しようと思い、以下の
コードを追加しました。

コード:

X509Certificate[] certs = (X509Certificate[])request.getAttribute(
"javax.servlet.request.X509Certificate");



しかし、以下の例外が発生してしまいます。
java.lang.ClassCastException: [Ljava.security.cert.X509Certificate;


試しに

コード:

Object cert = request.getAttribute(
"javax.servlet.request.X509Certificate");



として、クラスのキャストをせずにオブジェクトを取得してそのまま表示
すると、以下の文字列が表示されています。

[Ljava.security.cert.X509Certificate;@778255

これだけを見ると何かしらのオブジェクトが取得できている(上記の文字列を
見るとそれなりのオブジェクトが取得できているように見える)のですが、
なぜ、ClassCastException が発生してしまうのか分かりません。


TomcatでSSL3のクライアント認証を試された方がいらっしゃれば情報を頂け
ないでしょうか?


試している環境は以下のとおりです。

Windows2000
Tomcat 4.1.18
JDK1.3.1_07


[ メッセージ編集済み 編集者: ふうた 編集日時 2003-02-18 23:09 ]
まりり
ぬし
会議室デビュー日: 2001/12/05
投稿数: 329
投稿日時: 2003-02-20 00:51
> X509Certificate[] certs = (X509Certificate[])request.getAttribute(
> "javax.servlet.request.X509Certificate");

X509Certificate[]なんていう配列にキャストはできないですよ。

> Object cert = request.getAttribute(
> "javax.servlet.request.X509Certificate");

このあとでリフレクションなり使ってcertが実際には何なのか表示させてみては
いかがでしょう。

SSLとかなんとかの話ではないと思いますよ。
ふうた
大ベテラン
会議室デビュー日: 2001/08/23
投稿数: 198
お住まい・勤務地: 岡山
投稿日時: 2003-02-20 21:39
レスありがとうございます。

#以下長文になりますが…

引用:

まりりさんの書き込み (2003-02-20 00:51) より:
> X509Certificate[] certs = (X509Certificate[])request.getAttribute(
> "javax.servlet.request.X509Certificate");

X509Certificate[]なんていう配列にキャストはできないですよ。



すごい勘違いをしていました。。。

確かにJava 言語仕様に以下のように記述されていました。

引用:

If R is an ordinary class (not an array class):
 ・If T is a class type, then R must be either the same
  class as T or a subclass of T, or a run-time exception
  is thrown.
 ・If T is an interface type, then R must implement
  interface T, or a run-time exception is thrown.
 ・If T is an array type, then a run-time exception is
  thrown.


なお、R はキャスト元のクラス、T はキャスト演算子の型です。

キャスト元のクラスがObjectだから配列でも問題ないかと思い込んでいたのですが
そうではなかったのですね。


引用:

> Object cert = request.getAttribute(
> "javax.servlet.request.X509Certificate");

このあとでリフレクションなり使ってcertが実際には何なのか表示させてみては
いかがでしょう。



これに関しては

引用:

[Ljava.security.cert.X509Certificate;@778255



がクラス名かなと思ったので、配列にキャストするということをしてみたわけですが…

とりあえず、

コード:
Object certs = request.getAttribute(
  "javax.servlet.request.X509Certificate");
System.out.println(certs.getClass().getName());
System.out.println(Array.get(certs, 0).getClass().getName());



としてみると

[Ljava.security.cert.X509Certificate;
sun.security.x509.X509CertImpl

が表示されました。

ということで

コード:
Object certs = request.getAttribute(
  "javax.servlet.request.X509Certificate");
if (certs != null){
  sun.security.x509.X509CertImpl cert = 
    (sun.security.x509.X509CertImpl)Array.get(certs, 0);



とすることで、(期待したクラスとは違っていましたが)無事クライアント証明書
を取得できました。


ありがとうございました。


========以下、蛇足========================================

ちなみに上記のJava言語仕様には以下のようにも書いてありました。

引用:

If S is a class type:
 ・If T is a class type, then S and T must be related
  classes-that is, S and T must be the same class, or
  S a subclass of T, or T a subclass of S; otherwise
  a compile-time error occurs.
 ・If T is an interface type:
  If S is not a final class, then the cast is always
  correct at compile time (because even if S does not
  implement T, a subclass of S might).
 ・If S is a final class, then S must implement T, or
  a compile-time error occurs.
 ・If T is an array type, then S must be the class Object,
  or a compile-time error occurs.


S(変換元)はコンパイル時参照型 の値
T(変換先)はコンパイル時参照型 です。


どうせランタイムエラーになるのに、わざわざ変換元がObjectの時だけコンパイルエラー
にしていないのは何故でしょう?

コンパイルエラーにしてくれていれば、こんなに悩まずにすんだのに…と八つ当たりをして
みたり
うのきち
ベテラン
会議室デビュー日: 2003/02/17
投稿数: 55
投稿日時: 2003-02-20 23:38
Javaでは、配列もObjectなので、別に、Objectを配列にキャストしたってokですよ。
例えば、これは、エラーにならないはず。

Object obj = new String[]{"a", "b"};
String[] stringArray = (String[])obj;

で、今回の問題は、

java.security.cert.X509Certificate と、
javax.security.cert.X509Certificate を間違えているというのに一票。

つまり、
import javax.security.cert.X509Certificate;
って、やってません?

ふうた
大ベテラン
会議室デビュー日: 2001/08/23
投稿数: 198
お住まい・勤務地: 岡山
投稿日時: 2003-02-21 00:38
フォローありがとうございます。

引用:

うのきちさんの書き込み (2003-02-20 23:38) より:
java.security.cert.X509Certificate と、
javax.security.cert.X509Certificate を間違えているというのに一票。

つまり、
import javax.security.cert.X509Certificate;
って、やってません?



ビンゴです。まさにその通りでした。。。
import java.security.cert.X509Certificate;
とすることで元のコードでも動作しました。

ちなみになんで
import javax.security.cert.X509Certificate;
とやっていることが分かりました?(私の書き込みにjavaxという文字列はないですよね?)
もしかしてFAQですか?

あと
java.security.cert.X509Certificate
javax.security.cert.X509Certificate
の違いは何でしょう?後学のために教えてください。

P.S. 私が見たJava 言語仕様は何だったんでしょうか?(読み間違いですか? )
うのきち
ベテラン
会議室デビュー日: 2003/02/17
投稿数: 55
投稿日時: 2003-02-21 01:14
> ちなみになんで
> import javax.security.cert.X509Certificate;
> とやっていることが分かりました?(私の書き込みにjavaxという文字列はないですよね?)

X509Certificate[] certs = (X509Certificate[])request.getAttribute(
"javax.servlet.request.X509Certificate");

が、コンパイルエラーにならないのであれば、

java.security.cert.X509Certificate
javax.security.cert.X509Certificate

のどちらかがimportされていることが分かります(ま、"*"かもしれませんけど)。実行結果の、

[Ljava.security.cert.X509Certificate;@778255

は、java.security.cert.X509Certificateの配列を意味するので、これがClassCastExceptionになることから、javaxの方がimportされているのだと予想しました。

> java.security.cert.X509Certificate
> javax.security.cert.X509Certificate
> の違いは何でしょう?後学のために教えてください。

すんません、これは専門外なので分かりません。SUNは何で、こんな紛らわしいことをしたんでしょう。私も聞きたいです。

> P.S. 私が見たJava 言語仕様は何だったんでしょうか?(読み間違いですか?

あれは「ダウンキャスト無しでそのまま」代入出来る場合の規則です。
H2
ぬし
会議室デビュー日: 2001/09/06
投稿数: 586
お住まい・勤務地: 港
投稿日時: 2003-02-21 10:33
引用:

> java.security.cert.X509Certificate
> javax.security.cert.X509Certificate
> の違いは何でしょう?後学のために教えてください。

すんません、これは専門外なので分かりません。SUNは何で、こんな紛らわしいことをしたんでしょう。私も聞きたいです。


javax.security.cert.X509Certificate はJSSE(Java Secure Sockets Extension)がJSDK1.4のスタンダードライブラリに追加されたため、過去のJSSEとの互換性を保つために付け加えられています。java.security.cert.X509Certificate はJSDK1.2 からあるオリジナルというか、スタンダードライブラリに過去からあったやつです。

APIには java.security.cert.X509Certificate の方が機能的にも豊富なので、こっちを使うべきだとあります。

[ メッセージ編集済み 編集者: H2 編集日時 2003-02-21 10:35 ]
1

スキルアップ/キャリアアップ(JOB@IT)