シリーズ一覧
第1回:JDKのbinフォルダに入っているツールをまとめてみた
第2回:JDKのbinフォルダに入っているツールをまとめてみた 〜 セキュリティ
第3回:JDKのbinフォルダに入っているツールをまとめてみた 〜 国際化
JDKの bin フォルダの中を覗いてみるシリーズ、第2回は「セキュリティ」に分類されているツールたち、keytool, jarsigner, policytool についてまとめます。
以下のページの「セキュリティ」の項目のまとめです。
https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/intro.html#sthref20
セキュリティ
keytool
keytool は、暗号鍵や証明書の生成・管理をするツールです。
例えば、ある開発者がJARファイルを作成して配布することを考えた時、作成者のインターネット上への公開後に悪意のある第三者が不正なプログラムをそのJARファイルに混入させ、それをあたかも元のファイルであるかのように再配布する、という可能性が考えられます。
JARファイルの使用者は、手元にダウンロードしてきたファイルが確かに作成者によって固められた状態のままであることを検証する必要があり、また作成者も使用者が安全性を検証できる状態にしておく必要があります。
そのような場合に、安全性を保証するための手段が証明書と秘密鍵による署名です。
JARの作成者は keytool を使うことによって、作成者の情報を保持する公開鍵付きの証明書と、それをファイルに署名を付けるための秘密鍵を作ることができます。
以下は、新しい証明書と秘密鍵のペアを、"test_key" という名前のエイリアスで作成するコマンド例です。
$ keytool -genkeypair -alias test_key
キーストアのパスワードを入力してください:
姓名は何ですか。
\[Unknown]: chooyan_eng
組織単位名は何ですか。
\[Unknown]: personal
組織名は何ですか。
\[Unknown]: chooyan_eng
都市名または地域名は何ですか。
\[Unknown]: Tokorozawa
都道府県名または州名は何ですか。
\[Unknown]: Saitama
この単位に該当する2文字の国コードは何ですか。
\[Unknown]: JP
CN=chooyan_eng, OU=personal, O=chooyan_eng, L=Tokorozawa, ST=Saitama, C=JPでよろしいですか。
\[いいえ]: はい
<test_key>の鍵パスワードを入力してください
(キーストアのパスワードと同じ場合はRETURNを押してください):
ここで作成した秘密鍵と証明書を使い、後述する jarsigner ツールで実際にJARファイルへの署名を行います。
参考
- [keytool - Java Platform, Standard Editionツール・リファレンス] (https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/keytool.html#keytool_option_genkeypair)
jarsigner
jarsigner は、keytoolで作成した秘密鍵や証明書を使って JARファイルへ署名を付けるためのツールです。また、JARファイルの使用者は、同じく jarsigner コマンドを使うことでその安全性を検証することができます。
JARファイルの作成から署名、検証の流れを以下にまとめます。
jarファイルの作成
まずはサンプルとして、hello.jarファイルを作成します。
$ jar cf hello.jar dest/jp/co/chooyan/*.class
次に、作成したhello.jarファイルに対し、署名のチェックをしてみます。
$ jarsigner -verify hello.jar
jarは署名されていません。(シグネチャが見つからないか、構文解析できません)
この時点ではまだ署名はしていないため、「署名されていません」とメッセージが出ます。分かりやすいですね。
jarsigner を使って署名
では、jarsigner で署名をし、再度署名のチェックをしてみます。
$ jarsigner hello.jar test_key -tsa http://tsa.safecreative.org
キーストアのパスワードを入力してください:
jarは署名されました。
警告:
署名者の証明書は6か月以内に期限切れになります。
$ jarsigner -verify hello.jar
jarが検証されました。
今度は「jarが検証されました」と表示され、jarsigner で正常にhello.jarが署名されていることがチェックできました。
これで、JARの使用者も同じコマンドを実行することで hello.jar が安全であることをチェックすることができます。
JARの改竄
さて、今度はこの hello.jarファイルが作成後、悪意のある誰かに変更される状況を試してみます。
jar コマンドは -uf オプションでアーカイブ済みの JARファイルへの.classファイルの追加ができるので、それを利用してDummyLogic.classファイルを追加してみます。(DummyLogic は、JARを実行した瞬間悪意のある挙動をするプログラムである、という想定です)
$ jar -uf hello.jar dest/jp/co/chooyan/DummyServer.class
$ jarsigner -verify hello.jar -verbose
s k 284 Fri Aug 19 01:25:34 JST 2016 META-INF/MANIFEST.MF
446 Fri Aug 19 01:25:36 JST 2016 META-INF/MYKEY.SF
5876 Fri Aug 19 01:25:36 JST 2016 META-INF/MYKEY.DSA
0 Fri Aug 19 01:25:28 JST 2016 META-INF/
340 Fri Aug 19 01:06:40 JST 2016 dest/jp/co/chooyan/DummyServer.class
smk 232 Fri Aug 19 01:06:40 JST 2016 dest/jp/co/chooyan/HelloRemote.class
smk 340 Tue Aug 16 22:28:52 JST 2016 dest/jp/co/chooyan/HelloServer.class
s=シグネチャが検証されました
m=エントリがマニフェスト内にリストされます
k=1つ以上の証明書がキーストアで検出されました
i=1つ以上の証明書がアイデンティティ・スコープで検出されました
jarが検証されました。
警告:
このjarには、整合性チェックをしていない未署名のエントリが含まれています。
詳細は、-verboseおよび-certsオプションを使用して再実行してください。
「このjarには、整合性チェックをしていない未署名のエントリが含まれています」というメッセージが出ました。署名はしてあるけれども中身の一部が不正である、ということは、署名後に何らかの変更が加えられていることが分かりますね。
なお、上記の例では jarsigner で署名する際、タイムスタンプを付与する設定をしています(-tsa オプション)。これにより、JARの署名に使用した証明書の有効期限が切れる前に署名がされていることを証明することができます。
今回は、タイムスタンプに使用するURLは以下を利用しました。
https://tsa.safecreative.org/
参考
- https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/jarsigner.html
- http://docs.oracle.com/javase/jp/7/technotes/guides/security/time-of-signing.html
policytool
policytool は、Java実行時にJVMが読み込むポリシーファイルを編集するためのGUIツールです。
$ policytool -file java.policy
と実行すれば、java.policyファイルを読み込んで編集画面が立ち上がります。
なお、デフォルトの java.policy ファイルは $JAVA_HOME/jre/lib/security/ の中にあります。
Java では、このポリシーファイルに従ってプログラムがどのリソースやシステムプロパティにアクセスできるのかを制限する機能があります。特にアプレットなど、誰かが作った外部のソースコードを自分のマシンで実行するような場合には、そのプログラムが不正に自分のマシン上のファイルや設定を参照、変更してしまわないよう制限することが重要になります。
そのような設定を、この policytool で作成、編集することができるようになっています。
なお、ポリシーファイルは単純なテキストファイルですので、このGUIツールを使わなくても手作業で編集することが可能です。例えば、デフォルトのポリシーファイルは以下のようになっていまs。
$ cat java.policy
// Standard extensions get all permissions by default
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
// default permissions granted to all domains
grant {
// Allows any thread to stop itself using the java.lang.Thread.stop()
// method that takes no argument.
// Note that this permission is granted by default only to remain
// backwards compatible.
// It is strongly recommended that you either remove this permission
// from this policy file or further restrict it to code sources
// that you specify, because Thread.stop() is potentially unsafe.
// See the API specification of java.lang.Thread.stop() for more
// information.
permission java.lang.RuntimePermission "stopThread";
// allows anyone to listen on dynamic ports
permission java.net.SocketPermission "localhost:0", "listen";
// "standard" properies that can be read by anyone
permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor", "read";
permission java.util.PropertyPermission "java.vendor.url", "read";
permission java.util.PropertyPermission "java.class.version", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.version", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "file.separator", "read";
permission java.util.PropertyPermission "path.separator", "read";
permission java.util.PropertyPermission "line.separator", "read";
permission java.util.PropertyPermission "java.specification.version", "read";
permission java.util.PropertyPermission "java.specification.vendor", "read";
permission java.util.PropertyPermission "java.specification.name", "read";
permission java.util.PropertyPermission "java.vm.specification.version", "read";
permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
permission java.util.PropertyPermission "java.vm.specification.name", "read";
permission java.util.PropertyPermission "java.vm.version", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
};
詳細な設定方法、内容については以下のリンクを参考にしてください。
参考
- http://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/PolicyFiles.html
- java.security - ひしだま's ホームページ
まとめ
セキュリティに関するツールを一通り使ってみることで、Java開発時、実行時に起こりうるセキュリティ上の問題と、それに対するJavaとしての対策や考え方を学ぶことができました。
特にMavenを導入する前は、開発中に何も考えずにインターネットで拾ってきたJARファイルをインポートしたりしていたことがあったのですが、それがどれだけ危険なことであったか、というのもこの記事を書いていて実感しました。(無知って恐ろしい。。。)
引き続き、他のツールについてもまとめていきたいと思います。