クライアント認証
以前、apacheでhttpsを使うための設定を書いたが、今回はさらにhttpsのクライアント認証を行う方法について書いてみる。
- 準備。以前の内容に従って、ca.key, ca.crtができていること。
- クライアント用の証明発行要求の作成(クライアントのやること)
% openssl req -new -keyout client.key -out client.csr
Generating a 1024 bit RSA private key
......++++++
..++++++
writing new private key to 'client.key'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Kanagawa
Locality Name (eg, city) []:Yokohama
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Wizard limit
Organizational Unit Name (eg, section) []:client
Common Name (eg, YOUR name) []:client.wizard-limit.net
Email Address []:root@client.wizard-limit.net
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
これで、client.key, client.csr ファイルができる。パスフレーズは、適当に入れた。(後で使うので忘れないように)
- サインする。(CAのやること)
% ./sign.sh client.csr
CA signing: client.csr -> client.crt:
Using configuration from ca.config
Enter pass phrase for ./ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'JP'
stateOrProvinceName :PRINTABLE:'Kanagawa'
localityName :PRINTABLE:'Yokohama'
organizationName :PRINTABLE:'Wizard limit'
organizationalUnitName:PRINTABLE:'client'
commonName :PRINTABLE:'client.wizard-limit.net'
emailAddress :IA5STRING:'root@client.wizard-limit.net'
Certificate is to be certified until Mar 27 05:31:42 2004 GMT (365 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
CA verifying: client.crt <-> CA cert
client.crt: OK
これで、client.crt ができる。
- ブラウザに取り込めるように、crtファイルをPKCS#12形式に変換する。(クライアントの仕事)
% openssl pkcs12 -export -inkey client.key -in client.crt -certfile ca.crt -name client -out client.p12
Enter pass phrase for client.key:
Enter Export Password:
Verifying - Enter Export Password:
Exportパスワードは、ブラウザにimportするときに必要になる。
- apache の設定
WEBサーバの管理者に必要なファイルは、ca.crt のみ。httpd.conf で SSL の設定が正しく済んでいれば、以下の3行を変更すれば動くはず。(ca.crtを/usr/local/etc/apache2/ca/に置いた場合)
SSLCACertificatePath /usr/local/etc/apache2/ca
SSLCACertificateFile /usr/local/etc/apache2/ca/ca.crt
SSLVerifyClient require
とりあえず、ここまでの設定で apache を起動し、ブラウザに client の方の証明書をインストールしたところ、クライアント認証に成功した。
※ もし、特定のパスのみクライアント認証を行いたい場合は、SSLVerifyClientを<Location>の中に置く。
javaからアクセス
おまけに、javaからのhttpsアクセスについて書いてみる。
まず、普通にhttpsのURLにアクセスする方法。
import java.io.*;
import java.net.*;
public class Client {
public static void main(String args[]) throws Exception {
//System.setProperty("https.proxyHost", "プロキシーホスト名");
//System.setProperty("https.proxyPort", "プロキシーポート");
URL url = new URL(args[0]);
URLConnection con = url.openConnection();
con.connect();
InputStream in = con.getInputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = r.readLine()) != null) {
System.out.println(line);
}
r.close();
}
}
上のようなソースで、httpsにアクセスできる(はず)。
うまく行かない場合は、以下を疑ってみる。
- $JAVA_HOME/lib/security/cacerts が適切に更新されていない。
- httpsのサーバが、cacertsにある認証機関によって認証されていない。
おそらく、個人でやっている自前のサーバなどの場合は、ほとんど2に該当すると思うので、サーバの証明書又はサーバを認証しているCAの証明書をcacertsにインポートしてやる必要がある。
% cd $JAVA_HOME/lib/security
% keytool -import -keystore cacerts -alias サーバ名など -trustcacerts -file 証明書ファイル名
これで、再び上のソースを実行すれば、アクセスできるはず。あ、パスワードはデフォルトではchangeitらしい。
続いて、サーバがクライアント認証をかけていた場合。
- クライアントの鍵を作る。
% keytool -genkey -alias client -keystore mystore
キーストアのパスワードを入力してください: password
姓名を入力してください。
[Unknown]: client
組織単位名を入力してください。
[Unknown]: client
組織名を入力してください。
[Unknown]: sosiki
都市名または地域名を入力してください。
[Unknown]: yokohama
州名または地方名を入力してください。
[Unknown]: kanagawa
この単位に該当する 2 文字の国番号を入力してください。
[Unknown]: JP
CN=client, OU=client, O=sosiki, L=yokohama, ST=kanagawa, C=JP でよろしいですか?
[no]: yes
の鍵パスワードを入力してください。
(キーストアのパスワードと同じ場合は RETURN を押してください):
- 証明書要求を作る。
% keytool -certreq -keystore mystore -alias client -file client.csr
キーストアのパスワードを入力してください: password
これでできるclient.csrをサーバに送って、sign.shでサインする。
- 証明書をインポートする。
サーバでできるclient.crtは、keytoolではそのまま読めないみたいなので(ひょっとしたらオプションの指定で読めるのかも・・・)、-----BEGIN CERTIFICATE----- から-----END CERTIFICATE-----までの部分を切り出してclient.cerとして保存する。
% keytool -import -alias client -keystore mystore -trustcacerts -file client.cer
キーストアのパスワードを入力してください: password
ここまでで、クライアント用のキーとCAによって署名された証明書が入ったキーストア「mystore」が出来上がる。後は、javaを実行するときに、このファイルのパスとパスワードを教えてあげれば良い。
% java -Djavax.net.ssl.keyStore=mystore -Djavax.net.ssl.keyStorePassword=password https://www.wizard-limit.net/
おまけ
ブラウザとjavaクライアントで同じ秘密鍵-証明書を使いたい場合、javaのkeytoolでは秘密鍵のimport/exportができないので、javaでプログラムを書いて keystore から入出力してやれば同じ鍵が使えるようになる。
手順は、http://java-house.jp/ml/archive/j-h-b/051468.htmlからのメールのやり取りが参考になる。
false@wizard-limit.net