Chrome ver58以降の自己署名証明書(Self-signed certificate)のエラー対策 (net::ERR_CERT_COMMON_NAME_INVALID) : SAN追加

自己署名証明書(Self-signed certificate = いわゆるオレオレ証明書)を使ったWeb ServerにChromeの最新版(Ver 58 : 2017/04/28現在)でアクセスすると
`net::ERR_CERT_COMMON_NAME_INVALID`というエラーが出て接続ができなくなりました。その対策です。いろいろ情報は出ていますが、少数派と思われるWindows Server CAによる署名では情報が少なかったのでシェア。

時代の流れで常時TLSが一般的になりつつあり、LAN内でもWindows Server 2012 R2の証明機関(CA)機能による自己署名証明書を発行し、同じくLAN内のWeb Server (Apache2 on Linux)で利用していましたが、先週からChromeでエラーが出るようになりました。いろいろ調べてみると、最新版のChrome ver58以降から、Self-signedの証明書はエラーを出すらしく、いろいろ海外のサイト等で議論されていました。
私は、証明書(X.509v3)にマルチドメインなどに使うSAN (Subject Alternative Name) =日本語で「サブジェクト代替名」を拡張領域(フィールド)に追加し、そこにホスト名等を埋め込むことで対応することができました。この部分は、RFC2818で推奨されているようです。

<該当部分引用:from RFC2818>

If a subjectAltName extension of type dNSName is present, that MUST
be used as the identity. Otherwise, the (most specific) Common Name
field in the Subject field of the certificate MUST be used. Although
the use of the Common Name is existing practice, it is deprecated and
Certification Authorities are encouraged to use the dNSName instead.

<環境条件>
C1) LAN内オンプレのWindows Server 2012 R2にcertsrv (証明機関)および、IIS上にウェブサービスの`Microsoft Active Directory 証明書サービス`(以下Web CertSrv)が起動しているものとします。
C2) LAN内ウェブサーバーは、Apache2 (on CentOS7)とします。
C3) 証明書は、ECCなんぞハイカラなモノは避けて、RSA2048bitで保守的に。

<解説>
エラーが出る前までは、Web ServerのLinux上のOpenSSLでCSRを発行し、それをWindows ServerのWeb CertSrvで署名をして証明書をダウンロードし、Apacheのssl.confに追加していました。いろいろな海外サイトでは、Linuxサーバーの証明機関で自己署名をしているので、それでも完結(解決)できるはずですが、今回はWindows Server 2012 R2にリモートデスクトップで入り、その中にWindows版のOpenSSLを使ってCSRを作り、Web CertSrvで署名をしました。

<手順>
P1) Windows Server 2012 R2に管理者権限のあるアカウントでリモートログイン

P2) 管理者コマンドプロンプトにて、証明書へのカスタム属性対応を設定します。
c:\> certutil -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2

P3) 証明機関サービスを再起動
c:\> net stop certsvc
c:\> net start certsvc
→これで、内部CAが、SANの拡張領域に対応できます。

P4) Windows版OpenSSLのダウンロードとインストール:Shining Light Productions – Win32 OpenSSL
* 2017/04/28現在:Win32OpenSSL-1_1_0e.exe (WIN32 OpenSSL v1.1.0e)を入れました。これを書いていて気がついたのですがWin64の64bit版もあったのですね・・。Windows Server 2012 R2自体が64bit版なので、Win64を使うべきかと思います。私は気がつかずWin32版で行いました。C:\OpenSSL-Win32にインストール

P5) “C:\OpenSSL-Win32\bin\openssl.cfg”のコンフィグファイルを編集します。
[ v3_req ]という箇所を見つけて、以下の通り編集します。

[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]

DNS.1 = hoge.abc.yourdomain.com
DNS.2 = hoge2.abc.yourdomain.com
DNS.3 = abc.yourdomain.com

SANに追加したいホスト名・ドメインを追加して行きます。SANはマルチドメイン用のフィールドなので何個書いてもOKだと思います。私はWebセーバーのFQDN(LAN内ローカルドメイン)も入れておきました。

P6) コマンドプロンプト(管理者)で、”C:\OpenSSL-Win32\bin”に入ります。

P7) 秘密鍵の生成(RSA2048bit) : openssl genrsa 2048 > server.key

P8) 署名リクエスト(CSR)の作成 : openssl req -new -key server.key -config openssl.cfg > server.csr
-configのスイッチで上記で編集したopenssl.cfgを読み込んでCSRを生成します。
*ここで、Common Nameは、公開するウェブサーバーのFQDNを入れておくのが無難な気がします。たぶん。

P9) Windows Server 2012 R2 CAのWeb CertSrvにアクセスしますが、いろいろな理由で付属のIE11でアクセスするのがよさそうです(Windows Server 2012 R2へリモートデスクトップで入っている状態で、その中のIE11)。LAN内の他のPCからのChromeアクセスなどでは上手くうごきませんでした。

IE11にて:https://YOUR_AD_SERVER/certsrv
*TLS(SSL)でアクセスするのでエラーが出ても続行する。TLSでアクセスしないと署名ができない。

P10) SAN属性を入力して署名をする。

*CSRのBASE64で貼り付けるのはいつも通り。
*追加属性の部分に、以下のフォーマットでSANの情報を追記する。おそらくP2), P3)の作業をしていないとこの属性を入力しても証明書にSANが追加されないと思われる。

追加属性フォーマット(SAN): san:dns=hoge.abc.yourdomain.com&dns=hoge2.abc.yourdomain.com&dns=abc.yourdomain.com

と好きなだけ追加する。

P11) 送信ボタンを押して(CAで署名)をして証明書をダウンロードし(例えばcertnew.cer)、ダブルクリックして証明書を表示する。

*赤枠にあるように「サブジェクト代替名」(=SAN)で、追加されているのが分かります。

P12) P11)の証明書(server.cer)と、P7)の秘密鍵(server.key)をLinuxサーバーにコピーし配置します。
CentOS7であれば、/etc/pki/tls/certs/以下でしょうかね。パーミッションは600。

P13) Apache2のssl.confを修正しデーモンを再起動

SSLCertificateFile /etc/pki/tls/certs/server.cer
SSLCertificateKeyFile /etc/pki/tls/certs/server.key

P14) LAN内のいろいろなホストから最新のChrome (ver58以降)でアクセス https://FQDN/
無事にエラーがなく表示できれば成功。

*Active Directoryを使っていれば当然だと思いますが、自己署名証明書をクリアするために、CAをグループポリシーで配布してある前提です。(certmgr – 信頼されたルート証明機関 > 証明書にWindows ServerのCA証明書が入っているのは前提です。

*参考:
R0) Chrome Deprecates Subject CN Matching – text/plain
R1) Configure Internal Windows CA to issue SAN certificates
R2) FAQ/subjectAltName (SAN)
R3) ssl – Generating a self-signed cert with openssl that works in Chrome 58 – Server Fault

*備忘録メモ:IISサーバー証明書インポート向け.pfx作成方法
M1) openssl pkcs12 -export -inkey server.key -in server.cer -out server.pfx

IPv6対応

四苦八苦してこのサーバーもIPv6対応しました。日本のIPv6化は一体どうするのだろう・・。すんなり行くとは全く思えない。
とはいえDNSのAAAAレコードがふらふらでいまいち安定せず。
原因は、いままでいわゆるNaked Domainのspacewalker.jpというのを使っていましたが、AレコードとAAAAレコードをNakedに対応するようにワイルドカードを使うとこれがまた超不安定。
やはりwww.なり、何か入れないとIPv4, IPv6の現在のデュアル運用をせざるをえない日本の状況下では不安定すぎて困りもの。Nakedドメインでうまく行っている人いるのかな?(DNSが自前サーバーでないので細かく設定できないんだけども)
* その前にWindowsの名前解決がIPv6とIPv4の優先度がころころ変わっているのか、何もしていないのに突然接続不良になったりとイマイチ動きが掴めていないです。

もしIPv6アクセスを実現されているかたは、うちのトップページ
www.spacewalker.jp
にアクセスしただければ、下のようにIPv6の大きなロゴが表示されるはずです。
表示がないとIPv4アクセスです。どうでしょ?

20160105_WebIPv6

一応これでIPv6 + HTTP/2 + 常時TLSという今流行りの仕様が実現しているはず。ただスマホで使っているUQ-MobileがいまだIPv6未対応で困りもの。IIJ(MVNO)はいち早く対応しているようです。

*あまりに不安定なので暫定対応です。

公開鍵暗号と電子署名の復習

マイナンバーに付属の個人番号カード(マイナンバーカード)でも、利用されている公開鍵暗号電子署名技術。
そして、Let’s Encryptも含めウェブに関しては常時TLS化が始まっている時代。その認証基盤技術である公開鍵暗号と電子署名の基礎をステップごとに復習します。
公開鍵認証に関しては比較的新しいECC楕円などもありますが、いろいろな場面では未対応のシステムが多いことがわかり、2015年11月現在で標準的な
公開鍵暗号方式:RSA 2048 bit
共通鍵暗号方式:AES 256 bit
暗号学的ハッシュ関数SHA-2(SHA-256)
を用いることにします。SHA-2に関しては64bit環境では、SHA-512などが良いのでしょうが、スマートカード内CPUはまだまだ32bit環境も多いため、SHA-256とします。

*テスト環境:Cent OS 7 (64bit), OpenSSL 1.0.1e-fips 11 Feb 2013
*留意:2015/11/22時点での記事です。未来で参照する場合は最新技術、特に上記の方式なども使用禁止などの可能性があるのでご留意ください。

<その1:公開鍵暗号を用いてテキストファイルを暗号化・復号化>

1) 秘密鍵の生成(RSA 2048bit, X509 .PEMファイル)
# openssl genrsa 2048 > private_key.pem
秘密鍵`private_key.pem`ができました。(パスフレーズなしの秘密鍵)

2) 公開鍵の生成(秘密鍵より公開鍵を作成します)
# openssl rsa -pubout < private_key.pem > public_key.pem
公開鍵`public_key.pem`ができました。

3) それほど長くない暗号化するための平文テキストファイルを生成します。
# echo ‘hello, encrypt / decrypt world’ > test_plain.txt
text_plain.txtという31バイトの平文テキストファイルが生成されました。これを暗号化・復号化してみます。
それほど長くないという意味は、鍵長が2048bitなのでそれを超えるファイルサイズの物は原理的に暗号化できない。

4.1) 公開鍵で暗号化
# openssl rsautl -encrypt -pubin -inkey public_key.pem < test_plain.txt > test_plain.txt.encrypted
text_plain.txt.encryptedという暗号化されたバイナリファイルが生成されています(256バイト)
以上でテキストファイルを暗号化することができました。

4.2) 秘密鍵で復号化
# openssl rsautl -decrypt -inkey private_key.pem < test_plain.txt.encrypted > test_plain.txt.decrypted
text_plain.txt.decryptedというテキストファイルが生成されているはずです(31バイト)。
これで無事に元の平文テキストファイル(ファイルサイズも同じ)を復号化して生成することができました。

*補足:4.1), 4.2)は公開鍵で暗号、秘密鍵で復号でしたが、公開鍵暗号はその逆もできることが電子署名技術で重要な考え方です。つまり秘密鍵で暗号化されたものは、秘密鍵でのみ復号化できるという公開鍵暗号=非対称暗号の特徴です。
そこで試してみようと調べてみたらopenssl rsautlは秘密鍵で暗号、公開鍵で復号はできないようです。試してみたら復号化時に`A private key is needed for this operation`というエラーが出てしまいます。
つまり、秘密鍵での暗号に関しては、電子署名のみに使うことが前提となっているようです。
(非対称暗号の原理・特徴の前に実利用的なことを考えるとあたりまえかな。テストだけしてみたかったのですが)

*補足:あまり長いファイルを暗号化できないと上述しましたが、ではウェブのHTTPS(SSL/TLS)やSSHのリモートログイン時などはどうしているかというと、あくまで多量なデータ・ファイルの通信には暗号化・復号化の速度も速い共通鍵暗号を用いており、その共通鍵の受け渡しに今回の公開鍵暗号を使っているわけですね。

おまけ)共通鍵暗号(AES 256bit)を用いたファイルの暗号化・復号化
暗号化:
# openssl aes-256-cbc -e < test_plain.txt > test_plain.txt.AES256
暗号化するための共通鍵を問われますので何か入力して暗号化します。test_plain.txt.AES256という暗号化されたファイルが生成されます。

復号化:
# openssl aes-256-cbc -d < test_plain.txt.AES256 > test_plain.txt.AES256.decrypted
同じく復号化するための共通鍵を問われますので暗号化時に入力したものを入力します。test_plain.txt.AES256.decryptedというファイルで復号化されているはずです。

<その2:何らかのファイルに電子署名をし、そのファイルが途中で第三者に改ざんされていないかを確認する>

秘密鍵はその名の通り、厳密に秘密に管理され、その鍵を使用する個人しか持っていないとされている鍵です。これは言葉で書くのは簡単ですが、実際には上記でテストしたように秘密鍵をPC上などでファイルで扱うのは本当に`秘密`にしておけるのかというのが難しい問題です。そのPCがウイルスに侵されてファイルが盗まれたとか、もっと単純に他の人がログインをしてUSBメモリにコピーされたなど。その問題に対して、スマートカードやUSBトークンなどがあり、そのカード内に秘密鍵を保持し、その秘密鍵は取り出すことも複製もできなくすれば良いわけです。今回のマイナンバーにおける個人番号カード(スマートカード)も希望者にはマイナンバーの他に秘密鍵などが書きこまれた状態で取得できるはずです。最近のスマートカード(おそらく個人番号カードも)は内部にCPUを持っており、鍵生成・認証等の処理をカード内で行い、カードから秘密鍵等を端末まで出してこなくても処理ができるので、使用した端末に秘密鍵が残ることはほぼないはずです。NFCの電力伝送で非接触カード内CPUを動かしてRSAなどの演算をさせるのはやや大変な気がするので、個人番号カードはカードリーダーを用いる接触型が多いのではないかなと思います。しかし、スマートフォンのNFCで個人認証などをする未来を考えると、非接触でカード内認証ができるほうが良い様な気もします。スマートフォンにカードリーダーをつけるわけにはいきませんしどうなるのでしょう。FelicaやMIFAREなどの非接触カードで、公開鍵認証ができるものは無いような気がするのですが、そういう製品が出ているのですかね。マイナンバー特需でメーカーもがんばっているかもしれません。

さて、電子署名に関してですが、秘密鍵はその個人が唯一持っている鍵であることと、公開鍵暗号の非対称性を利用して行うものです。つまり秘密鍵で暗号化されたものは、公開鍵で復号できる特徴です。
(電子署名技術はいろいろ種類がありますが、有力である公開鍵暗号方式に基づくデジタル署名ということで話を進めます)
基本的な流れを復習すると

(い)Aさんが大切な文章をWordファイルで作成。(今回たまたまWordファイルを例にして紹介していますが、どんなファイルでも同じです)
(ろ)Aさんは、秘密鍵を使ってWordファイルで暗号化する。
(は)Aさんは、取引先のBさんに、Wordファイル原本と、暗号化したファイルの2つを渡す。
(に)Bさんは、届いた2つのファイルの内、暗号化されているファイルを公開されているAさんの公開鍵で復号する。
(ほ)Bさんは、Wordファイル原本と、Aさんの公開鍵で復号したWordファイルを比較する。
(へ)2つのファイルがまったく同じものだったら、「これはAさんが作成されたものに間違いなく、他の人が改変などしていない」ことと判断できる。

となるわけですね。これは秘密鍵がAさんしか持っていない特徴によって実現できるものです。
ただし一般的に‘電子署名をする‘といったら、上記の方法の様にWordファイル全体を暗号化して送ることはしません。公開鍵暗号で大きなファイルを暗号化するのは処理的にも重いものですし、Bさんに2つのファイルを転送するのは転送量も2倍(以上)になり、また巨大な2つのファイルをバイナリ・ビットレベルで比較するのも大変であるからです。
そこで、暗号化ハッシュ関数を用います。Wordファイル(Wordに限らずどんなファイルでもOK)にハッシュ関数を計算すると、Wordファイルの大きさにかかわらず同じ長さのハッシュ値(メッセージダイジェストとも表現される)を取得できます。ファイルの内容が1bitでも異なるとこのハッシュ値は異なりますので、1つのファイルに対して確実に1つだけのハッシュ値を生成できます。またハッシュ値からファイル本体を復元(復号)することはできません。つまり上記の(い)~(へ)を暗号化ハッシュ関数を用いると

(い)Aさんが大切な文章をWordファイルを作成し、またハッシュ関数でそのファイルのハッシュ値を生成。
(ろ)Aさんは、ハッシュ値を秘密鍵を使って暗号化。そのことを‘電子署名する‘と言います。「このファイルの電子署名をお願いします」と頼まれたらハッシュ値を取ってそれを秘密鍵で暗号化することを意味します。
(は)Aさんは、取引先のBさんにWordファイル原本と、(ろ)で生成した電子署名(ハッシュ値を暗号化したもの)の2つを渡す。
(に)Bさんは、受け取った電子署名を、Aさんの公開鍵で復号し、Aさんが生成したハッシュ値を取得します。
(ほ)Bさんは、受け取った原本のWordファイルのハッシュ値を求め、(に)で取得したハッシュ値と比較します。
(へ)2つのハッシュ値がまったく一緒だったら、「これはAさんが作成されたものに間違いなく、他の人が改変していたらハッシュ値もかわるはずなので、改変はない」と判断できる。

以上を踏まえ、OpenSSLで電子署名の動きを実際に確認してみます。

5) 上記の例に沿ってWordファイルを用意します。
5.1) 正しいWordファイル(test_word1.docx : 835666バイト)
5.2) 正しいWordファイルを改変したファイル(test_word2.docx : 835812バイト)

6) 正しいWordファイルをSHA-256でハッシュを取得してみます(参考)
# sha256sum test_word1.docx
ハッシュ値:b37c7c542a5af29ef6edb5e1cb2406385121c46e1b406361d80e9e6d6c5df9f6
*本来はこのハッシュ値を秘密鍵で暗号化すれば電子署名になりますが、上述のとおり秘密鍵で暗号化がOpenSSLではできないのでこのハッシュ値の取得は参考例です。

7) 正しいWordファイルをSHA-256でハッシュ値(ダイジェスト)を求め、秘密鍵で暗号化します(つまり電子署名します)
# openssl dgst -sha256 -sign private_key.pem test_word1.docx > test_word1.sign
test_word1.signというバイナリファイル(256バイト)が生成され、電子署名となります。

8) ここからは別の人が2つのWordファイル(正しいもの、改変されたもの)、そして7)の電子署名を受け取ったことにして、電子署名を公開鍵を用いて検証します。
– 正しいファイル(test_word1.docx)の場合
# openssl dgst -sha256 -verify public_key.pem -signature test_word1.sign test_word1.docx
Verified OKと出て検証OKです。

– 改変ファイル(test_word2.docx)の場合
# openssl dgst -sha256 -verify public_key.pem -signature test_word1.sign test_word2.docx
Verification Failureと出て、改変された(少なくとも電子署名をしたファイルではない)ことが分かります。

まとめ
・公開鍵暗号、電子署名をOpenSSLを用いて実際の動作をステップ毎に確認しました。
・今回秘密鍵生成を‘パスフレーズなし‘で行いましたが、実利用では秘密鍵の使用を制限するパスフレーズを設ける必要があります。スマートカードならPIN(Personal Identification Number)になります。
・実際にはOpenSSLを実装したシステム・ソフトが勝手にこの作業を行ってくれています。
・公開鍵に関して雑に扱いましたが、公開鍵は信頼できる第三者機関(Trusted Third party)が信頼できる公開鍵ですよとして公開されている(つまり電子証明書=公開鍵証明書)のが望ましく、それが前提となっています。
・日本において印鑑は血判状なども含め大切にされてきました。一方で今のグローバル時代には非効率であるという点は避けられない事実です。電子署名は法律的にも認められていますし、印鑑の良さを残しつつ、スマートな契約等が進むと良いですね。