Firefoxと認証局証明書(信頼されたルート証明機関)

Firefoxは、Chromeとは違い、デフォルトで信頼する認証局(トラストストア;Windowsで言う「信頼されたルート証明機関」)が内部にハードコードされている。一方、ChromeはWinでもMacでもOSのトラストストアを参照する。

ということで、Firefoxソースコードからこのリストを取り出そうとしたのだけど、いろいろ面倒だったのでメモ。特にOpenSSLはトラストストアを持たないため、Firefoxの(すなわちMozillaの)トラストストアを流用するケースなどを想定。

Firefoxのソースと認証局証明書

Firefoxのデフォルトの認証局証明書は、ソースツリーでは以下のcertdata.txtにある。

しかし上記ファイルは、次のように一般的な形式(PEMなど)では書かれておらず、OpenSSLなど他のアプリでそのまま利用することはできない。

#
# Certificate "GlobalSign Root CA"
#
# Issuer: CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE
# Serial Number:04:00:00:00:00:01:15:4b:5a:c3:94
# Subject: CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE
# Not Valid Before: Tue Sep 01 12:00:00 1998
# Not Valid After : Fri Jan 28 12:00:00 2028
# Fingerprint (MD5): 3E:45:52:15:09:51:92:E1:B7:5D:37:9F:B1:87:29:8A
# Fingerprint (SHA1): B1:BC:96:8B:D4:F4:9D:62:2A:A8:9A:81:F2:15:01:52:A4:1D:82:9C
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
CKA_TOKEN CK_BBOOL CK_TRUE
CKA_PRIVATE CK_BBOOL CK_FALSE
CKA_MODIFIABLE CK_BBOOL CK_FALSE
CKA_LABEL UTF8 "GlobalSign Root CA"
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
CKA_SUBJECT MULTILINE_OCTAL
\060\127\061\013\060\011\006\003\125\004\006\023\002\102\105\061
\031\060\027\006\003\125\004\012\023\020\107\154\157\142\141\154
\123\151\147\156\040\156\166\055\163\141\061\020\060\016\006\003
\125\004\013\023\007\122\157\157\164\040\103\101\061\033\060\031
\006\003\125\004\003\023\022\107\154\157\142\141\154\123\151\147
\156\040\122\157\157\164\040\103\101
END
CKA_ID UTF8 "0"
CKA_ISSUER MULTILINE_OCTAL
\060\127\061\013\060\011\006\003\125\004\006\023\002\102\105\061
\031\060\027\006\003\125\004\012\023\020\107\154\157\142\141\154
.....(省略).....

これは、Firefoxのビルド時にこのcertdata.txtからcertdata.cが作られてコンパイルされるため、Cで扱いやすい形式で書かれた歴史的理由による(たぶん)。ということでこれを解決するにいくつかの方法がある。

curlプロジェクトの配布物を利用

curlhttpsにも対応していることから、トラストストアと密接に関連している。

ということで、curlプロジェクトが独自にcertdata.txtをPEMに加工したものを配布している。

これは比較的定期的に更新されており、ちゃんとメンテナンスされているため、特に理由が無ければこれを使えばいい。しかし、世の中にはもちろん、「オレはcurlプロジェクトは信頼ならん」という人もいるだろうから、そういう人は自分でcertdata.txtを加工する必要がある。

変換ツールを利用(Go-lang)

Adam Langley氏の書いた以下のツールが有名で使いやすい。

READMEに書かれているとおり、certdata.txtを置いたディレクトリでgo run convert_mozilla_certdata.goするだけ。

もちろん、Adam Langley氏も信用ならんということであれば、自分でツールを書くしかない。この際にはまる罠を紹介しておく。

Distrustの罠

自前でcertdata.txtの加工スクリプトを書く場合、ハマりがちなのがこのDistrustの罠である。

certdata.txtは、やっかいなことに「信頼されない(Distrust)証明書」もまぜこぜにして格納されている。有名なモノは、侵入者により偽証明書が発行されてしまったDigiNotarのものである。

#
# Certificate "Explicitly Distrust DigiNotar Root CA"
#
# Issuer: E=info@diginotar.nl,CN=DigiNotar Root CA,O=DigiNotar,C=NL
# Serial Number:0f:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff
# Subject: E=info@diginotar.nl,CN=DigiNotar Root CA,O=DigiNotar,C=NL
# Not Valid Before: Fri Jul 27 17:19:37 2007
# Not Valid After : Mon Mar 31 18:19:22 2025
# Fingerprint (MD5): 0A:A4:D5:CC:BA:B4:FB:A3:59:E3:E6:01:DD:53:D9:4E
# Fingerprint (SHA1): C1:77:CB:4B:E0:B4:26:8E:F5:C7:CF:45:99:22:B9:B0:CE:BA:21:2F
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
CKA_TOKEN CK_BBOOL CK_TRUE
CKA_PRIVATE CK_BBOOL CK_FALSE
CKA_MODIFIABLE CK_BBOOL CK_FALSE
CKA_LABEL UTF8 "Explicitly Distrust DigiNotar Root CA"
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
CKA_SUBJECT MULTILINE_OCTAL
\060\137\061\013\060\011\006\003\125\004\006\023\002\116\114\061
\022\060\020\006\003\125\004\012\023\011\104\151\147\151\116\157
\164\141\162\061\032\060\030\006\003\125\004\003\023\021\104\151
.....(省略).....

上記の、「CKA_LABEL」に"Distrust"があることに注意して欲しい。つまりこれは、信頼してはいけないルート証明書なのだ。そのため自作ツールでは、これらを取り除く処理をしないといけない。いやはやめんどい。

Google Chrome (Chromium)を自分でビルドする

いろいろとやる必要があり、Chromiumを手元でコンパイルしたのでその記録。

(2018-06-23) @tzik_tackさんに指摘いただき、一部修正しました。ありがとうございます。

ChromeChromium

まず、予備知識。

Chromiumオープンソースプロジェクト名であり、そのプロダクトがChromiumというWebブラウザ。そして、ChromiumをベースにGoogleが固有機能を追加したものが、我々がふだん使っているGoogle Chromeである。

もっとも、Chromiumプロジェクト自体Googleが深く関わっているし、普通にそこでの実装の意思決定もGoogleのエンジニアによるものが多いので、実質どっちもGoogleがやってるという理解でおおむね問題ない。

Google Chrome自体はGoogleの著作物なので、自分でビルドするならばChromiumをビルドする、ということになる。

ビルド準備

基本的には、ドキュメントに書いてある通りにやればいい。ここに私が書いてあることもすぐ陳腐化すると思うので、ドキュメントを読もう。

ビルドには、Ubuntuを使った方が良い。WindowsでもVisual Studio Community 2017でビルドできるけど、Core i5, RAM 8GB, SSDのマシンでsln(プロジェクトファイル)開くだけで1時間、ビルドは10時間かけても終わらなかったので、やめた方がいい。

なおドキュメントには「8GB以上のメモリ(16GB以上を推奨)、100GB以上のディスク空き容量」が必要と書かれているので頑張ろう。私はESXi上の、Ubuntu 16.04.4を使った。

ビルドに必要なパッケージはだいたいソースコード一式に同梱されているので、事前準備としてはpython, build-essential, gitくらいあればいいはず。(最近のChromeはClang使っているが、chromiumリポジトリに同梱されているためclangパッケージは不要)

ビルド手順

depot_tools

はじめに、ビルドに必要なdepot_toolsというリポジトリをcloneする。

$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

この中にある色々なツールを使うので、PATHを通しておくこと。たとえば.bashrcに以下のように加える。

export PATH="$PATH:/home/ozuma/local/depot_tools"

続いて適当な作業ディレクトリ内で、dep_toolsのfetchコマンドでソースコードを取得する。この際、--no-historyを付けて最新版のみ取得すると良い。

$ fetch --nohooks --no-history chromium

ドキュメントのとおり、このfetchには30分ほどかかる。するとsrcディレクトリができるので移動する。

$ cd src

ここまでで、ソースコードのcheckoutができた。続いてUbuntuの場合、依存性を解決してくれるシェルスクリプトが用意されているので実行する。

$ sudo build/install-build-deps.sh

追加パッケージ入手。

$ gclient runhooks

ここまでで、ビルドの準備が整った。

Ninjaビルド

Chromeのビルドは、depot_tools付属のninjaというコマンドで行う。ninjaについては以下。

Ninjaがビルドするためのディレクトリout/Defaultを、depot_tools付属のgnコマンドで作成する。

$ gn gen out/Default

さて、このままビルドしても良いのだが、それだと大変に時間がかかるのでここでいくつかオプションを加える。gnコマンドにargsを付けてオプションを設定する。

$ gn args out/Default

ドキュメントに書いてあるが、まずJumbo buildsという設定をすると早くなる。多くのソースコードがヘッダファイルを共有しているのだから、それらを一緒にコンパイルする……と書いてあるけど、具体的な動きはよく分からない。

また、NaCl(Native Client)をビルドしないようにする。

それから、デバッグ用に使うわけではないの、デバッグシンボルを全部OFFにする。

これらの設定は、上記gn argsコマンドを打つとvimが起動するので、そこに以下のように記述する。実際には、out/Default/args.gnファイルに書かれる。

use_jumbo_build=true
enable_nacl=false
symbol_level=0
remove_webcore_debug_symbols=true
is_debug=false
is_component_build=true

最後に、ninjaコマンドでビルドする。timeコマンドを頭に付けて、どのくらいの時間がかかるか見ておいた方がいい。

ozuma@ubuntu16:~/local/depot_tools/src$ time ninja -C out/Default chrome
ninja: Entering directory `out/Default'
[19686/19686] LINK ./chrome

real    331m41.029s
user    320m3.768s
sys    8m46.260s
ozuma@ubuntu16:~/local/depot_tools/src$

私の手元では、全ビルドに5時間半ほどかかった。Windowsに比べればメチャクチャ早い。

Chromiumの起動

できあがったバイナリはout/Default/chrome。これをたたくと、ちゃんと起動した。

おしまい。

特定のバージョンのビルド

上記の手順では最新のビルド(Canary相当)が入るので、Stableなど古いバージョンを指定してビルドする場合。

上記にやり方が書いてあるが、この通りだとエラーを吐く。

で言われているように、

$ gclient sync --with_branch_heads --with_tags -Rv --disable-syntax-validation

と-Rv --disable-syntax-validationを指定する必要がある。(syntax validation無しでいいのだろうか)

これでタグが持ってこれたので、タグを指定してチェックアウトする。以下のようになる。

$ git checkout -b branch_67.0.3396.87 67.0.3396.87

(Gitでは、コミットIDの代わりにタグ名を指定してcheckoutできる)

なおgclient syncは、checkoutしたあとに依存性の更新をするためにもう一度実行する必要がある。

あとは同じようにビルドする。

$ gclient runhooks
$ gn gen out/67.0.3396.87
$ gn args out/67.0.3396.87
$ time ninja -C out/67.0.3396.87 chrome

個人でEV SSL証明書は取れるのか (1)

それなりにSSL証明書マニアなので、昨日から、「個人でEV SSL証明書を取るにはどうすればいいか」というアホなことを調べている。

まず、認証局が厳密に従っている(はずだよね?)の、CA/Browser Forumのドキュメントを確認した。EV SSL証明書の発行プロセスについては、Baseline Requirementsの方では無く、「About EV SSL」として以下に分かりやすくまとめられている。

ここで発行対象を見ると....

Certification Authorities (CAs) may only issue EV SSL Certificates to Private Organizations, Government Entities, Business Entities and Non-Commercial Entities that satisfy the requirements specified below.

ということで、以下の4つにしかEV SSL証明書は発行できない。

  1. Private Organizations
  2. Government Entities
  3. Business Entities
  4. Non-Commercial Entities

うん。個人ではやっぱダメだね。私は政府組織関係ないから(2)はダメだ。というわけで方針変更して、個人事業主もしくは、主催している「すみだセキュリティ勉強会」で取れないかを考えてみる。

で、(4)の「Non-Commercial Entities」の条件はキツすぎる。

The Applicant is an International Organization Entity, created under a charter, treaty, convention or equivalent instrument that was signed by, or on behalf of, more than one country’s government.

最後の「more than one country’s government.」が強すぎて、日本ならNPO法人として登録し、さらに複数の国....つまりどこでもいいから別の国でも登録しないといけない。ムリムリムリ。ちなみに日本でNPO法人EV SSL証明書を取っている例では、JIETというところがあるんだけど、

ここはバンコク支部やソウル支部を持っているので、この条件はクリアしているのだろう……。

というわけで「Private Organizations」。これ、普通の会社では、帝国データバンクのやDUNS(東京商工リサーチ)の登録番号出したり、法務局に行って法人登記簿謄本やら取ってくるんだけど....個人だとあれか、マイナンバーカードならいいのか。あれなら政府お墨付きだから文句は言わせんぞ。

もしくは、個人事業主の開業届も、政府(国税庁)の公文書だからよいのでは。とか屁理屈を考えたけど、comodoもdigicertも「個人事業主はダメ」ってはっきり書いてある。

うーん。次回に続く。(続かないかも)

追記(2018-08-26)

解決しました。

すみだセキュリティ勉強会2018その1を開催しました

長い間のブランクをあけてしまいましたが、主催しているすみだセキュリティ勉強会を久々にやりました。

私のお話は、emoji(絵文字)を使ったパスワードの話です。

前々から日本語もパスワードに許せばいいのにと思っていたのですが、どうせUnicodeなんだったら絵文字も突っ込んじゃえばどうだろ? と思ったのが元ネタです。理屈上は、Unicodeを正しく扱っていれば、日本語だろうがASCII文字だろうが絵文字だろうが単なる「1文字」なので、最近のモダンな環境なら普通に通るはずですね。

なおemojiなどの面0(BMP)以外のUnicode文字は、既知の通りMySQLで多くの地雷が埋まっています。寿司ビール問題くらいならまだいいのですが、INSERT時にemoji以降が切り捨てられるという現象が起こる場合もあります。
これがパスワードで発生すると非常に困った事態になるのですが、少なくともパスワードについてはハッシュ化するなりして捻って格納しているはずですから、切り捨て問題は起きないはず。平文でパスワードを保存していたら知らんけど。

ようやく復活できたので、隔月くらいでまた開催したいなー。

補足

勉強会後に気づきましたが、既にGCP(Google Cloud Platform)で、絵文字をパスワードに使ってもいいんじゃね? 的な話が出てました。

こうした点を踏まえると、ユーザーが使いたいあらゆる文字をパスワードに使用できるようにするべきです。Klingon や Emoji、両端に空白を含む制御文字をパスワードに含めたいユーザーがいたとしても、それを拒む技術的理由はないはずです。

これは、NIST SP 800-63Bを受けてのようです。

5.1.1 Memorized Secrets
For purposes of the above length requirements, each Unicode code point SHALL be counted as a single character.