前のページへ

サーバ編

次のページ





HTTPSサーバ (HTTPS servers)

20132月にHttpServerインターフェイスにbindSecureというクラス・メソッドが追加され、HTTPSサーバの開発が可能となった。20159月にDartチームは 1.13.0-dev.1.0版からDart SDK及びDart VMTLS/SSLとしてBoringSSLを実装したと発表した(これまでのdart:ioの暗号化ライブラリではApacheなどで使われているOpenSSLではなくてNetwork Security Services (NSS)を採用していた)。BoringSSLOpenSSLの派生物でGoogleが作成・管理している。従ってDartの開発者たちはChromeと同じSSL実装を使用することになっているBoringSSLOpenSSLに比べてコンパクトであり、よりきちんとまた積極的に更新・維持されている。PEMファイル(通常認証機関から署名つき証明書としてメールで送られてくる標準的なテキスト形式)を使って認証とキーの管理がなされ、NSSで使われているセキュリティ・データベースに比べてより理解し易いものとなっている。



簡単なサンプルを試してみる

取りあえずHTTPサーバを試してみたいという読者には、Dartチームが教材用に用意したhttpserver/bin/hello_world_server_secure.dartというサーバを試されたい。このサーバと以下の2つのPEMファイルがあれば、簡単にHTTPサーバを走らせることができる。

手順は以下のようになる:

  1. dart-tutorials-samplesのリポジトリをダウンロードする

  2. ダウンロードしたdart-tutorials-samples-master.zipファイルを解凍し、IDEでそのフォルダを開く

  3. httpserverのフォルダの中の/bin/hello_world_server_secure.dartを開く

  4. windowsの場合は以下のようにPEMファイルのパスを変更する:

    String certificateChain = 'bin/server_chain.pem';
    String serverKey = 'bin/server_key.pem';
  5. このコードを実行させる

  6. ブラウザからhttps://localhost:4047でこのサーバをアクセスする

  7. そうするとブラウザは以下のようにその応答を表示する(Chromeの場合):




  8. ここで詳細設定をクリックし、さらに「localhost にアクセスする(安全ではありません)」を選択すると以下のようにテキストが表示される:




  9. 一旦ブラウザにこのサイトの情報が記録されると、以後何回も繰り返しリロードしても手順7で示した画面は表示されなくなる。

このような画面が出るのはこのサンプルのPEMファイルにある認証情報が正式な認証機関から得られたものではなく、自己証明書(いわゆるオレオレ証明書)であるためである。実際のサイトの場合は正式な証明書を取得する必要がある。

PKIの基礎

TLS/SSLの基となっている技術はPKIPPublic Key Infrastructure、「公開鍵基盤」と訳される)である。従ってまずPKIの基礎を略説する。詳細は日本語の資料が豊富に存在する(例えば情報処理推進機構のPKIに関する資料)ので、それを見ていただきたい。

PKIは次の2つの要素で構成される:

  1. 2つの鍵のペアを使う暗号化技術。これは一方の鍵で暗号化したメッセージはもう一方の鍵でのみ複合できる特殊な暗号化技術で、一方の鍵を公開鍵として相手に渡し、他方の鍵を秘密鍵として自分が持つことで、自分のみが相手が送信したメッセージを受け取ることが可能となり、盗聴を防止する。この暗号化方式は送り手と受け手が同じ鍵を持つ「共通鍵方式」と対比して「公開鍵方式」(あるいはより正確には「非対象アルゴリズム暗号方式」)と呼ばれる。

  2. 電子署名のメカニズム。相手(例えばサーバ)が本当に自分がメッセージを送ろうとしている正しい相手なのかどうか(即ち渡された公開鍵が正しい持ち主のものなのか)を確認するもので、上記の暗号化技術を応用している。これによりなりすましや改ざん等の不正に対処する。確認のためには認証局(CA)と呼ばれる機関が発行し署名した証明書(電子署名)が使われる。サーバは認証局が発行した証明書(秘密鍵で暗号化した)と公開鍵をクライアントに渡し、クライアントは次のように相手を確認する:

    1. 相手の公開鍵を入手する

    2. その公開鍵で送付された電子署名を復号化する

    3. 送付された平文から、相手と同じアルゴリズムを用いてハッシュを作成する

    4. 2の結果と3で作成したハッシュを比較し、一致することを確認する

電子証明書の中身はざっと以下のようなものからなる:

  1. 登録された公開鍵

  2. その公開鍵の持ち主の情報

  3. 証明書を発行した認証局の情報

  4. 発行元認証局の署名

電子証明書の詳細なフォーマットについては、ITU-Tが定めたX.509という書式が使われる。

認証局は数多く存在しており、システム設計者はそのなかから選択できる。トップ階層にある認証局を「ルート(Root)認証局」という。例えばIEでは「ツール」メニュー→「インターネットオプション」→「コンテンツ」の証明書ボタン」を押すと、現在ブラウザで使用できる証明書が表示される。Chromeでは「設定」→「詳細設定」→「証明書の管理」で同じ画面が得られる。この中の「信頼されたルート証明機関」というタブで表示される証明書の認証局はすべてルート認証局であり、あらかじめブラウザに「信頼する認証局」として登録されていることを示している。表示された証明機関のどれかをダブルクリックすれば、その詳細を知ることができる。




TLS/SSLの基礎

TLS (Transport Layer Security)は上記のPKIHTTPSocket通信に適用させるものである。TLSはその名のとおりトランスポート層を安全なものとする為の仕掛けであり、ネットワーキングのプロトコル下位層のTCP層の上に介在させるプロトコルである。基になった技術がSSL (Secure Sockets Layer)であるため、むしろSSLという言葉が良く使われる。詳細は別途説明するが、とりあえずはWikipediaなどを参照されたい。

下図はそのプロトコルの位置づけである:




TLSRFC2246としてTCPの上に置かれ、またHTTPアプリケーションとの仲介にHTTPプロトコルが存在するが、そのHTTPそのものもTLS対応の為にRFC 2817でアップグレードされている。TLSは接続型のプロトコルで、クライアントとサーバ間で安全(セキュア)な接続を確立し、その接続上でデータ(HTTPメッセージ)を交換する。TLS対応のブラウザはTLSクライアントになり、サーバのしかるべきTCPポート(デフォルトは443Tomcat8443)上への接続を開始し、TLSハンドシェイクを始めるために、TLS ClientHelloメッセージを送る。TLSハンドシェイクが完了すれば、接続が確立されたことになり、その接続上でクライアントは最初のHTTP要求をサーバに送信できるようになる。

クライアントとサーバ間で安全(セキュア)な接続を確立ためのTLSの主要な要素は、前節で示したように暗号化と認証である。認証に際してはサーバはサーバ証明書(Certificate)、あるいは加えてサーバ側が信頼する認証局のリストをブラウザに送信してそのサーバが合法であることを提示する。一般のTLS対応のアプリケーションでは、お互いが正当かどうかに関しては、サーバは認証局(CA)が発行する認証局の認証済みサーバ証明書で確認されるが、クライアントは認証局が出す公開鍵を使うだけである。このままではサーバから見たクライアントは、信頼できる相手と確信できない。そのためアプリケーションのレベルでこれまでの認証と同じようにユーザ名とパスワードによる認証を使うことが出来る。しかし、TLSではその為のクライアント認証が用意されている。

「クライアント認証」(CLIENT-CERT認証)では、更に安全性を高める為に、サーバがクライアントに対し自分が誰であるかを証明させる。サーバにSSLで接続する際、クライアントに証明書を提示させ、接続元を認証する。通常、サーバーが信用する認証局が署名した電子証明書をクライアントが提示してはじめてSSLの接続が成立する。TLS対応のサーバの殆どはこのクライアント認証をクライアントに要求していない。クライアント認証に関しては日本ベリサイン社の担当者が投稿した記事が判り易く説明されており、参考になろう。クライアント認証は通常電子商取引や企業内網で使用されている。

TLSの基本シーケンス

TLSのシーケンスに関しては、日経NETWORKの半沢智氏の記事が判り易く書かれているので、それを読んでいただいたほうが良かろう。



TLSの基本シーケンスは次のようである:

  1. ネットワーク上のノードのサーバとクライアントが証明書を交換することで互いを特定し確認する。証明書はVeriSignのような認証局によって発行される。サーバのID、サーバの公開鍵、及び認証局の署名などを含んだこの証明書は、認証局が持っている秘密鍵によって暗号化されている。そうするとクライアントは認証局の公開鍵を使ってこの証明書を復号し、サーバの公開鍵を取得できる。公開鍵暗号方式では,一方の鍵で暗号化したデータは,ペアとなっているもう一方の鍵でしか復号できない(これを非対象アルゴリズム暗号方式という)。公開鍵で暗号データを正しく復号できた場合,その暗号データはペアとなっている秘密鍵を使って暗号化されたことが保証される。秘密鍵は,所有者だけが持っているはずなので,これによってその証明書は認証局が発行した証明書であることがわかる。これで、クライアントは証明書から取得したサーバの公開鍵が信用できるものであることが判るので、これを使ってサーバのメッセージを復号化出来る。同じようにサーバはクライアントの公開鍵を取得できる。ただウェブ・アプリケーションの場合は、クライアント(ブラウザ)がサーバを信頼できるものであるかどうかを判断するだけの方法が一般的である。従って、以下そのような手順を説明することとする。

  2. クライアントはサーバに対して取得したサーバの公開鍵で暗号化されたランダムなデータ(プレマスタ・シークレット)を送る。クライアントは自分が生成した該プレマスタ・シークレットをもとに共通鍵を生成する。サーバは受けた暗号化されたデータを自分の秘密鍵を使って復号する。サーバは復号化されたランダム・データをもとにクライアントと同じ共通鍵を生成する。これでクライアントとサーバがともに同じ鍵(共通鍵)を持ったことになる。

  3. クライアントとサーバは電子的にメッセージ交換時の暗号化と復号に使うアルゴリズムの折衝を行う。折衝されたアルゴリズムはクライアントとサーバの双方が受け付けられるものでなければならない。

  4. 共通鍵を使って双方がデータの交換を行う。即ち、サーバはサーバ鍵を使って暗号化したデータをクライアントに送信し、クライアントはそれをサーバ鍵を使って復号する。クライアントはクライアント鍵を使って自分が送りたいデータを暗号化しそれをサーバに送り、サーバをそれを受けたらクライアント鍵を使って復号化する。

実際のネットワーク上のTCPレベルのシーケンスはRFC 2246によれば以下のようになっている:



TLSのシーケンス

SSLクライアント

(ブラウザ)

SSLサーバ


ユーザがhttps://で始まるアドレスをクリック

SYN

――→

TCP_Port = 443

このセッションには安全化された(secure)接続が必要である。クライアントはHTTPS TCPポート番号443TCP接続を確立する

SYN+ACK

←――

ACK

――→

新しいTCP接続上でのSSLハンドシェイク

HELLO_REQUEST

←――

サーバは何時でもこのメッセージを送信し、ハンドシェイクの手順を最初からやり直すことをクライアントに要求できる

CLIENT_HELLO

――→

Highest SSL Version,

Ciphers Supported,

Data Compression Methods,

SessionId = 0,

Random Data

クライアントはハンドシェイクの最初にまずCLIENT_HELLOメッセージを送信するが、これには以下のものが含まれる:

  • このクライアントが対応している最も新しいSSLTLSのバージョン

  • このクライアントが対応している暗号化アルゴリズムたち。これは好ましいもの順でリスト化されている

  • このクライアントが対応しているデータ圧縮法たち

  • このセッションのID。もしそのクライアントが新しいセッションを開始するときはこのセッションID0

  • ランダム・データはクライアントが生成し、これはキー生成プロセスで使用される

SERVER_HELLO

←――

Selected SSL Version,

Selected Cipher,

Selected Data Compression Method,

Assigned Session Id,

Random Data

サーバはCLIENT_HELLOメッセージを受けてサーバはこのSSLセッションで使用する暗号方式などを決定し、SERVER_HELLOメッセージをクライアントに送信するが、これには以下のものが含まれる:

  • このSSLセッションの為に使われるSSLまたはTLSのバージョン

  • このSSLセッションで使う暗号化アルゴリズム

  • このSSLセッションで使用するデータ圧縮

  • このSSLセッションの為のセッションID

  • サーバが生成したランダム・データで、キー生成プロセスで使われる

SERVER_CERTIFICATE

←――

Public Key,

Authentication Signature

サーバはサーバ認証の為のSERVER_CERTIFICATEメッセージを送信する。このコマンドは次のものを含む:

  • このサーバの証明書

  • このサーバの証明書を割り当てた認証局の証明書で始まる証明書たちのチェイン

CLIENT_CERTIF_REQUEST

←――

サーバはCLIENT_CERTIF_REQUESTメッセージを送信し、クライアント認証を求めることができる。

SERVER_HELLO_DONE

←――

サーバはSERVER_HELLO_DONEメッセージを送信する。このコマンドはこのサーバがこのSSLハンドシェイクのフェイズを完了したことを示す。

CLIENT_CERTIFICATE

――→

SERVER_HELLO_DONEメッセージを受けたらクライアントは最初にこのメッセージを送信できる。これはサーバがクライアント認証を求めたときにのみ送信される。このメッセージで送信されるものはSERVER_CERTIFICATEと似て以下のものとなる:

  • このクライアントの証明書

  • オプションとしてこのクライアントの証明書を割り当てた認証局の証明書で始まる証明書たちのチェイン

サーバの証明書を検証する




クライアントの証明書を検証する


CERTIFICATE_VERIFY

←――

サーバはクライアントに対しそのクライアントの証明書を検証したことを知らせる。このメッセージはクライアント認証が求められているときに送信される

CLIENT_KEY_EXCHANGE

――→

PKCS #1エンコードされたプレマスタ・シークレット(Premaster Secret)で、サーバからSERVER_CERTIFICATEメッセージで受理した公開鍵で暗号化されている。このプレマスタ・シークレットからサーバはクライアントにデータを送信するための鍵を生成する。先頭に置かれるヘッダ・ハンドシェークは暗号化されない

CHANGE_CIPHER_SPEC

――→

クライアントはCHANGE_CIPHER_SPECメッセージを送信する。このメッセージはこのセッション中にこのクライアントが送信するこれからのSSLデータ・レコードの中身が暗号化されることを通知するものである。5バイトからなるSSLレコード・ヘッダは暗号化されない

FINISHED

――→

クライアントはFINISHEDメッセージを送信する。このメッセージはこの時点までのクライアントとサーバ間で交わされた総てのSSLハンドシェイク・コマンドたちのダイジェストを含む。このメッセージにより、クライアントとサーバ間で暗号化されないでこれまで交わされたメッセージたちのどれもが途中で誰かによって変えられていないことを確認する為に送信される。

CHANGE_CIPHER_SPEC

←――

サーバはCHANGE_CIPHER_SPECメッセージを送信する。このメッセージはサーバが送信するこれからのSSLデータ・レコードの中身が暗号化されることを通知するものである。

FINISHED

←――

サーバはFINISHEDコメッセージを送信する。このメッセージはこの時点までのクライアントとサーバ間で交わされた総てのSSLハンドシェイクメッセージたちのダイジェストを含む

これ以降クライアントとサーバーは,共有したプレマスタ・シークレットから共通鍵を生成し,その共通鍵を使って暗号通信を実施する

この表で赤で示したメッセージがCLIENT-CERT認証に関わるものである。

OpenSSLとそのインストール

これまでのdart:ioの暗号化ライブラリではApacheなどで使われているOpenSSLではなくてNetwork Security Services (NSS)を採用していた。NSSでは証明書や鍵はcert9.dbkey4.dbなどといったデータベースに置かれていた。一方OpenSSLではPEMファイルが使われ、Apacheではキーストア(keystore.jks)も使用されている。またコマンド行ツールはNSSではcertutilであり、一方OpenSSLではopensslあるいはkeytoolである。

新しいBoringSSL対応のライブラリでもPEM(.pem)ファイルが使われるが、これは.dbファイルをPEM形式にエクスポートできる。従ってNSSをで使っていた鍵と証明書が既に存在する場合は、その手順に従えばよい。

PEM(Privacy-enhanced Electronic Mail)書式は認証局が証明書を発行する際に使われる最も一般的な書式である。拡張子は .pem, .crt, .cer, 及び .keyである。これらはBase64エンコードされたASCIIファイルで、例えば公開鍵の場合は"-----BEGIN CERTIFICATE-----""-----END CERTIFICATE-----"文で挟まれたテキストである。サーバ証明書、中間証明書、プライベート鍵などもこの書式に変換できる。

OpenSSLに関する日本語の解説は多くの認証機関またはそれらの代理事業者たちが提供している(例えばDigiCert社のサーバ証明書に関する解説)ので、それを参照して頂きたい。

ダウンロードとインストール

ここではWindows用のWin32 OpenSSL v1.0.2d Light64ビット機でも32ビット版をインストールすることが推奨されている)をダウンロードしてインストールした例である。ここではNSSと同じc:\securityというディレクトリに配置してある。

インストーラ画面(インストールの場所指定)




openSSLのインストール




基本的なコマンド

opensslコマンドの詳細は公式サイトのマニュアル(あるいは日本語のサイト)を見ることになるが、かなり専門的である。基本的なopensslコマンドは次のようである(下線が引かれた個所はユーザが設定するもの):

  1. 一般的なコマンド

CSR(証明書署名要求)、証明書、プライベート・キーの生成などのタスク

  • 新規プライベート・キーと証明書署名要求の生成

openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key

  • 自己署名の証明書の生成

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt

  • 既存のプライベート・キーに対する証明書署名要求(CSR)を生成する

openssl req -out CSR.csr -key privateKey.key -new

  • 既存の証明書に基づいた証明書署名要求(CSR)を生成する

openssl x509 -x509toreq -in certificate.crt -out CSR.csr -signkey privateKey.key

  • プライベート・キーからパス・フレーズを削除する

openssl rsa -in privateKey.pem -out newPrivateKey.pem

  1. OpenSSLを使ってチェックする

証明書、証明書署名要求、あるいはプライベート・キーのなかの情報をチェックしたいときにこれらのコマンドを使う。他のオンライン・ツールを使ってCSRや証明書をチェックすることも可能である(例として日本語のサイト)。

  • CSR(証明書署名要求)をチェックする

openssl req -text -noout -verify -in CSR.csr

  • プライベート・キーをチェックする

openssl rsa -in privateKey.key -check

  • 証明書をチェックする

openssl x509 -in certificate.crt -text -noout

  • PKCS#12ファイル(.pfx or .p12)をチェックする

openssl pkcs12 -info -in keyStore.p12

  1. OpenSSLを使ってデバッグする

プライベート・キーが証明書と合致しないとかあるサイトにインストールした証明書が信頼されないとかのエラーを受けた場合は、これらのコマンドのなかのひとつを試されたい。あるSSL証明書が正しくインストールされたかを確認したいときは、SSL Checkerを試されたい。

  • パブリック・キーのMD5ハッシュがCSRまたはプライベート・キーのなかのそれと合致するかをチェックする

openssl x509 -noout -modulus -in certificate.crt | openssl md5

openssl rsa -noout -modulus -in privateKey.key | openssl md5

openssl rsa -noout -modulus -in privateKey.key | openssl md5

openssl req -noout -modulus -in CSR.csr | openssl md5

  • SSL接続をチェックする。総ての証明書(仲介も含む)が表示される

openssl s_client -connect www.paypal.com:443

  1. OpenSSLを使って変換する

以下のコマンドはサーバあるいはソフトウエアのタイプに適合させるために証明書と鍵を別の書式に変換させるものである。例えば、ApacheDartで動作する通常のPEMファイルをPFX (PKCS#12)に変換してTomcatIISで動作するようにできる。SSLコンバータを使うとより簡単に変換できる場合がある。

  • DERファイル(.crt .cer .der) PEMに変換する

openssl x509 -inform der -in certificate.cer -out certificate.pem

  • PEMファイルをDERに変換する

openssl x509 -outform der -in certificate.pem -out certificate.der

  • プライベート鍵と証明書を含んだPKCS#12 ファイル(.pfx .p12)PEMに変換する

openssl pkcs12 -in keyStore.pfx -out keyStore.pem -nodes

-nocertsを追加してプライベート鍵のみ変換したり、-nokeysを付加して証明書のみ変換したりできる

  • PEM証明書ファイルとプライベート鍵をPKCS#12 (.pfx .p12)に変換する

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt

秘密鍵と証明書の用意

筆者の「改定サーブレット・チュートリアル」の第15章参照の「認証局からの証明書の取得」の項で示したように、認証局(CA)から証明書を取得する為には、先ず自分の秘密鍵を用意し、それをもとに証明書署名要求(CSR: Certificate Signing Request)を作成しなければならない。

最初にHTTPSサーバ構築に必要なファイル名やパスワード等をあらかじめ決めておくことをお勧めする。以下はその例である:

秘密鍵のファイル名

my_key.pem

認証局署名つきサーバ証明書のファイル名

my_cert.pem

証明書署名要求のファイル名

my_csr.pem

秘密鍵にたいするパスフレーズ(必要なら)

changeit

また最初にコマンドプロンプト上で次のようにディレクトリの移動とパス設定を行う:

cd c:\security\openssl-win32\bin\pem

path c:\security\openssl-win32\bin

秘密鍵と証明書署名要求(CSR: Certificate Signing Request)を作成する

前節で示したように、opensslツールでは秘密鍵の生成と証明書署名要求の作成を同時に行うことが可能であるがその場合はパスフレーズの入力が要求されないことに注意。

openssl req -out my_csr.pem -new -newkey rsa:2048 -nodes -keyout my_key.pem

以下はその実行例である:

c:\security\OpenSSL-Win32\bin\PEM>openssl req -out my_csr.pem -new -newkey rsa:2048 -nodes -keyout my_key.pem

Loading 'screen' into random state - done

Generating a 2048 bit RSA private key

...+++

..........................................................+++

writing new private key to 'my_key.pem'

-----

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]:Tokyo

Locality Name (eg, city) []:Minato-ku

Organization Name (eg, company) [Internet Widgits Pty Ltd]:Cresc

Organizational Unit Name (eg, section) []:System

Common Name (e.g. server FQDN or YOUR name) []:Terry

Email Address []:


Please enter the following 'extra' attributes to be sent with your certificate request

A challenge password []:

An optional company name []:


c:\security\OpenSSL-Win32\bin\PEM>

幾つかの入力項目があるがこれは、新しい証明要求と証明書の所有者を特定する情報で、RFC-1485に準拠して指定(CN=..., OU=...)する。Cは国名、CNは一般名(Common Name (eg, YOUR name))、Oは会社名(Organization Name)、OUは部門名(Organizational Unit Name)などである。メール・アドレス、チャレンジ・パスワード、オプショナルな会社名はスキップすればよい。

これでmy_key.pem及びmy_csr.pemの二つのファイルが生成される。

my_key.pemの内容は次のコマンドでチェックできる:

c:\security\OpenSSL-Win32\bin\PEM>openssl rsa -in my_key.pem -check

RSA key ok

writing RSA key

-----BEGIN RSA PRIVATE KEY-----

MIIEowIBAAKCAQEA6YV6R0zApfofoY8DX7ws7FebeFBsflx/w5S4hFxQ64BjgK7s

kPLSLCtIybZmms/H8p0AQRBfkP8oVY/m+YsBSWm50C2Sz1wYQ9pC0uZWH99SPfRR

IC+xnsTjym3q/Ht+7mO39keqRhqvr+LHkC+q/MyX9swc2ealXTVVXwir2TE0uCMi

MviILop6WCU5uU5u3Yc3NdMigcX08OMzTfxGsBYsobna/4tMSELSquiYYt6VJMZa

5N2UYK3XCiGIAh7gaz5JV1Tr5ayqO2wLLP+15Y6z7dXuvl01lBD+qzjImfuKwVAk

irkRODh4T4gia3ToJqoEITg59yMQozYbUwIK5wIDAQABAoIBAF+xevJM2YUqglvK

Jy/MBPy0ydj72/nMHe8fup1C10YqTpwlEnwzhTzkX+eI/3LhQqaA/+Gpu//HD9hA

J/6Kn/Rdhu9wPYEL1EW54zhZe2GEOkd5HuV5pefR9ya09F6SnOlDo9cgdv5TYTtK

S00iu4bssDlKW2hPw+P2Pw26l/FgGD2Npcjd+/zKRBof6ZXIXM3R918ThTAc9v2O

tn+zKGuT/ADU1nsfq2yOgz2dry1mZ/Mut5ok6zlDNCY6cnqlGIrcZfhWz183uBiX

4/+Pr/7hzELxDxplQvm5lMlK1dzmr4PXOz0THVjgHECvat5MHKoz9Gw81NYZCCYt

bmGkDAECgYEA9jdo1nHfYa0mR6B6OtxnXVtkZpC57X2ilnPD1K9sTHUN/DP8ir/3

nftWoYYkYzIHZ5MeqG9uunKiQaoIDz3S651je39uT0U9SOitaRFTndEpmjPGFsja

2DKa+3RyOpVCqMP0LwyP/Rz/scSN0aGg5Npnf/uosmnTLoIW4SzBuicCgYEA8szu

HrMbZFMefUGoXg45LJjy84+379CkosraFpH2xdpe0qk2kjZeAjiLCl1q8IdXPcwB

bUiz0Go1jOWkQW5H9d9FVFF82A0JM0KT9f9r43mSrFOsE/ELbS0HJZRFr5MhPRg2

1G6LDDay+Bp2ICg++bSTXQY02QtjRQtVyOoPYUECgYEAl8Z6Us7pQ53n3fl3v3cE

JNFkp7EJW/O5WQNNsXrdyJfToictwx4o9vLraTB2l8tMMzXGjU/7suVdThsRUsvq

jCF+JK/eAPGOoLYFx6HNqJg6C/tkXcE83k77qIwUqjY+XChHCwW0cPQCTsP6JEJ9

GvXjHUq1qB2B98Zrci0T0rsCgYAlye9lp8qjmqwsIoPp35zWbBekAMJH+Nkm3RuE

V8Nol8waTWvI5d1LyzEYs+Fo/id93rp6H86cqWscOsGNzXQ1uEI6FVSw65Z6++m7

Z72K8ej6GSu3DtUQcOmj16fg461QrOwbs/jANeM06iloZ9slUg19dPHUtgkFPfZk

BsnOAQKBgFOipZlfUmBmjIKeWaA87H/GZFfSnNLRYFmRwJY6UhGTm6X/v2A/qZRW

9t+joWCWPasuJ1TUzVz9xRunFnUR9d0HE7pZSk+tXNl2giNvm4QAubQg2x8y+voC

G0aRqy300De7TsyG9tZJweGjXUTiKcjnwSQ3PIizRG3W1LAEs5Aa

-----END RSA PRIVATE KEY-----

my_csr.pemは次のコマンドでその内容を確認できる:

c:\security\OpenSSL-Win32\bin\PEM>openssl req -in my_csr.pem -text

Certificate Request:

Data:

Version: 0 (0x0)

Subject: C=JP, ST=Tokyo, L=Minato-ku, O=Cresc, OU=System, CN=Terry

Subject Public Key Info:

Public Key Algorithm: rsaEncryption

Public-Key: (2048 bit)

Modulus:

00:e9:85:7a:47:4c:c0:a5:fa:1f:a1:8f:03:5f:bc:

2c:ec:57:9b:78:50:6c:7e:5c:7f:c3:94:b8:84:5c:

50:eb:80:63:80:ae:ec:90:f2:d2:2c:2b:48:c9:b6:

66:9a:cf:c7:f2:9d:00:41:10:5f:90:ff:28:55:8f:

e6:f9:8b:01:49:69:b9:d0:2d:92:cf:5c:18:43:da:

42:d2:e6:56:1f:df:52:3d:f4:51:20:2f:b1:9e:c4:

e3:ca:6d:ea:fc:7b:7e:ee:63:b7:f6:47:aa:46:1a:

af:af:e2:c7:90:2f:aa:fc:cc:97:f6:cc:1c:d9:e6:

a5:5d:35:55:5f:08:ab:d9:31:34:b8:23:22:32:f8:

88:2e:8a:7a:58:25:39:b9:4e:6e:dd:87:37:35:d3:

22:81:c5:f4:f0:e3:33:4d:fc:46:b0:16:2c:a1:b9:

da:ff:8b:4c:48:42:d2:aa:e8:98:62:de:95:24:c6:

5a:e4:dd:94:60:ad:d7:0a:21:88:02:1e:e0:6b:3e:

49:57:54:eb:e5:ac:aa:3b:6c:0b:2c:ff:b5:e5:8e:

b3:ed:d5:ee:be:5d:35:94:10:fe:ab:38:c8:99:fb:

8a:c1:50:24:8a:b9:11:38:38:78:4f:88:22:6b:74:

e8:26:aa:04:21:38:39:f7:23:10:a3:36:1b:53:02:

0a:e7

Exponent: 65537 (0x10001)

Attributes:

a0:00

Signature Algorithm: sha256WithRSAEncryption

d2:92:b1:70:07:1a:d1:67:c8:83:c4:f0:7e:15:f7:ad:26:77:

87:b8:5a:b4:e3:6e:1e:31:64:99:f0:28:7f:90:6c:4f:c5:c6:

(以下省略)

ここにはPublic-Key: (2048 bit)としてRSA2048ビットの公開鍵、そしてSignature AlgorithmとしてSHA256変換した256バイトの秘密鍵のハッシュ値が含まれていることが理解されよう。

信頼される認証機関からの証明書の取得

Verisignなどの公式の認証機関から証明書を取得するにはBEGINの行のつぎからENDの行の前までをコピー/ペーストして渡すことになる。詳細は各認証機関の取得手順に関する解説に従うこと。

通常そのような機関からはメールで署名つき証明書が.cer.pemといった拡張子で送信されてくる。「改定サーブレット・チュートリアル」の第15章参照の「認証局からの証明書の取得」の項を参照のこと。次の例はVeriSignから取得したテスト用の有効期間が短期間の証明書である。

その詳細は長いので最初の部分のみを示すと次のようになっている:

Certificate:

Data:

Version: 3 (0x2)

Serial Number:

06:af:57:23:51:13:28:28:2e:2d:d3:2d:ca:6b:d2:3e

Signature Algorithm: PKCS #1 SHA-1 With RSA Encryption

Issuer: "CN=VeriSign Class 3 Secure Server 1024-bit Test CA,OU=Terms

of use at https://www.verisign.com/cps/testca/ (c)07,OU=VeriSign

Trust Network,OU=FOR TEST PURPOSES ONLY,O="VeriSign, Inc.",C=US"

Validity:

Not Before: Sat Apr 09 00:00:00 2011

Not After : Sat Apr 23 23:59:59 2011

Subject: "CN=localhost,OU=Terms of use at www.verisign.com/cps/testca

(c)05,OU=Tech,O=Cresc,L=Mita,ST=Tokyo,C=JP"

Subject Public Key Info:

Public Key Algorithm: PKCS #1 RSA Encryption

RSA Public Key:

Modulus:

bd:11:dd:3b:22:32:82:d3:9e:de:3f:2a:56:a9:83:ad:

05:29:75:20:d7:6a:a7:c1:83:b5:4d:d1:61:c5:8a:00:

ba:ee:d9:a4:94:f7:f1:3d:eb:a9:10:1e:11:ee:f8:b1:

4e:1b:e8:28:e9:d2:c8:d1:b7:30:19:e3:f8:58:bd:c8:

89:c3:9e:00:24:29:db:78:3d:61:60:ed:42:f7:2a:a9:

e2:82:69:71:69:16:a1:fe:fd:b1:cf:09:11:35:3d:2f:

08:b3:cb:85:bf:79:1d:3b:4d:dd:0d:a4:da:50:9c:d9:

95:40:fa:93:f9:49:53:00:1a:71:2e:4e:bd:60:36:09

Exponent: 65537 (0x10001)

Signed Extensions:

Name: Certificate Basic Constraints

Data: Is not a CA.


Name: Certificate Key Usage

Usages: Digital Signature

Key Encipherment

(以下省略)

これはこの公開鍵の所有者が信用できることをVeriSignが証明した詳細な内容である。

自己証明書の作成(Self-signed Certificate)

本来は「改定サーブレット・チュートリアル」の第15章参照の「認証局からの証明書の取得」の項で示したように、作成した証明書発行要求書をもとにした証明書を認証局から取得しなければならない。しかしながら先ずブラウザが警告を出すものの自分で証明した自己証明書(いわゆるオレオレ証明書)を使って、手っ取り早くSSL接続の実験を進めることとする。

コマンドは次のようになる:

openssl x509 -req -signkey my_key.pem -in my_csr.pem -days 3650 -out my_crt.pem

以下はその実行例である:

c:\security\OpenSSL-Win32\bin\PEM>openssl x509 -req -signkey my_key.pem -in my_csr.pem -days 3650 -out my_crt.pem

Loading 'screen' into random state - done

Signature ok

subject=/C=JP/ST=Tokyo/L=Minato-ku/O=Cresc/OU=System/CN=Terry

Getting Private key

ここでは10年間の有効期間を設定している。

その内容は次のように確認できる:

c:\security\OpenSSL-Win32\bin\PEM>openssl x509 -text -in my_crt.pem

Certificate:

Data:

Version: 1 (0x0)

Serial Number:

b9:cd:48:c9:ff:54:1e:e0

Signature Algorithm: sha256WithRSAEncryption

Issuer: C=JP, ST=Tokyo, L=Minato-ku, O=Cresc, OU=System, CN=Terry

Validity

Not Before: Oct 15 04:05:58 2015 GMT

Not After : Oct 12 04:05:58 2025 GMT

Subject: C=JP, ST=Tokyo, L=Minato-ku, O=Cresc, OU=System, CN=Terry

Subject Public Key Info:

Public Key Algorithm: rsaEncryption

Public-Key: (2048 bit)

Modulus:

00:e9:85:7a:47:4c:c0:a5:fa:1f:a1:8f:03:5f:bc:

2c:ec:57:9b:78:50:6c:7e:5c:7f:c3:94:b8:84:5c:

途中省略)

e8:26:aa:04:21:38:39:f7:23:10:a3:36:1b:53:02:

0a:e7

Exponent: 65537 (0x10001)

Signature Algorithm: sha256WithRSAEncryption

c1:64:e9:11:62:a9:3d:a7:35:40:d5:08:2e:f6:94:a0:10:9c:

66:87:7c:1c:61:00:5e:ea:d6:c5:b9:57:fc:83:f7:17:69:c2:

途中省略)

70:1a:c0:d2:9f:b6:6c:fc:db:ef:d4:ab:19:58:84:80:37:ba:

7f:2d:2e:4d

-----BEGIN CERTIFICATE-----

MIIDQDCCAigCCQC5zUjJ/1Qe4DANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJK

UDEOMAwGA1UECAwFVG9reW8xEjAQBgNVBAcMCU1pbmF0by1rdTEOMAwGA1UECgwF

途中省略)

t85B6NxTzBzSbEJQXj+MSo/NHUtWJXhbcApcQEe4PIeNMdezcs2zSyrOpbPBHXAa

wNKftmz82+/UqxlYhIA3un8tLk0=

-----END CERTIFICATE-----

以上でつのファイルがc:\security\OpenSSL-Win32\bin\PEMのフォルダのなかに作られたので、これを自分のアプリケーションのしかるべきフォルダの中に置けばよい:

  • my_csr.pem

  • my_key.pem

  • my_crt.pem



HTTPSサーバの実験

簡単なHTTPSサーバを実験してみよう。このhttps_serversというアプリケーションGitHubからDownload ZIPのボタンをクリックしてダウンロードできる。なおHTTPSサーバのプログラム開発にはこの章の最初に紹介したDartチームが作成したサンプルも参考になる。

このアプリケーションをダウンロードしてインストールし、pubspec.yamlを選択してPub : Get Dependenciesを実行して、必要なライブラリを取り込む。そうすると自分のIDE上には次のようなファイル構成が表示される:

https_serversの構成




  • サーバの実行ファイルは/binのなかに置かれる。

  • BoringSSLPEMファイルたちは/opensslのなかに置かれる。

  • アプリケーションに必要な静的リソースは/resourcesのなかに置かれる。

  • いつものようにパッケージ・マネージャはpubspec.yamlを調べて、必要なファイルを生成している。

https_test_server_1

https_test_server_1.dartはブラウザがこのサーバに正常にHTTPSアクセスできたときに簡単なメッセージを返す。https_test_server_1.dartを実行させると、エディタのコンソールには次のようなメッセージが表示され、NSSライブラリの初期化が完了して、このサーバがクライアントからのHTTPS要求を受け付けることが可能になっていることが判る:

10:49:57.508713 - BoringSSL security context initialized.

10:49:57.562718 - https_test_1 server started.

ここでChromeブラウザからHTTPS://localhost/testをアドレス・バーに入れてアクセスさせると、最初の節で記したようにブラウザのインスタンスは最初にlocalhostHTTPSでアクセスした場合は警告を出す。これはここで使われている証明書が自己証明書であり、信頼された認証機関から発行されたものでないからである。VeriSignなどの信頼された認証機関からの証明書を既に取得しているユーザは、自分のmy_crt.pem及びmy_key.pemを上書きコピーし、ニックネームをパスワードをそれにあわせて指定してやれば、ブラウザはこのような警告を出さない。

ブラウザの画面は次のようなものになる:




https_test_server_1.dartのコードは次のようになっている:

import 'dart:io';

final LOG_REQUESTS = true;       // set true for debugging
final HOST_NAME = 'localhost';   // use loop back address for the test
final int SERVER_PORT = 443;     // use well known HTTPS port number
final REQ_PATH = '/test';        // request path for this application
final CERT_PATH = 'openssl/my_crt.pem'; // path to pem cert file
final KEY_PATH = 'openssl/my_key.pem';  // path to pem private key file
final KEY_PASSWORD = 'changeit';        // password for the key file
SecurityContext serverContext;   // security context of this server

void main() {
  setSecurityContext();
  listenHttpsRequest();
}

void setSecurityContext() {
  serverContext = new SecurityContext()
    ..useCertificateChain(CERT_PATH)
    ..usePrivateKey(KEY_PATH, password: KEY_PASSWORD);
  log('BoringSSL security context initialized.');
}

void listenHttpsRequest() {
  HttpServer.bindSecure(HOST_NAME,
                        SERVER_PORT,
                        serverContext)
  .then((HttpServer server) {
    server.listen(
      (HttpRequest req) {
        if (req.uri.path.contains(REQ_PATH)) processRequest(req);
        else {
          req.response.statusCode = HttpStatus.badRequest;
          req.response.close();
        }
      },
      onError: (err) {
        print('listen: error: $err');
      },
      onDone: () {
        print('listen: done');
      },
      cancelOnError: false
      );
    log('https_test_1 server started.');
  });
}

以下省略

幾つかのポイント列記すると:

  • final変数は次のようになっている。

    • final LOG_REQUESTS = true; // ログを取るかどうかを指定する。実験の場合はtrueのままとする

    • final HOST_NAME = 'localhost'; // テスト用にはループバック・アドレスを使用する。本番には指定されたグローバル・アドレスをセットする。

    • final int SERVER_PORT = 443; // ここではポート番号はHTTPS用に指定されている443を使用する。Apacheなどでは実験用に8443を使う場合が多いが、その場合はブラウザのアドレス・バーにはhttps://localhost:8443/testとポート番号を指定してやらねばならない。

    • final REQ_PATH = '/test'; // ここでは/testという要求パスを持った要求のみを取扱う。

    • final CERT_PATH = 'openssl/my_crt.pem'; // PEM証明書ファイルへのパス

    • final KEY_PATH = 'openssl/my_key.pem'; // PEM秘密鍵ファイルへのパス。これらの2つのパスは相対パスで指定しているが、無論絶対パスで指定しても良い。なおWindows以外のOSの場合は、それに準拠したパスの指定の仕方をしなければならない。

    • final KEY_PASSWORD = 'changeit'; // PEM秘密鍵ファイルにアクセスするためのパスワード

  • サーバの起動に先だって、setSecurityContextメソッドを使ってDart VMが用意しているSecurityContextの初期化をしておかねばならない。これによりBoringSSLは証明書と秘密鍵場所と秘密鍵のパスワードを知り、クライアントからのSSL接続要求に対応できるようになる。

  • 同じくサーバの起動に先だって、HttpServer.bindSecureという静的メソッドを使ってSSL接続からのHTTP要求を受理するように設定する。

  • その後の処理は基本的に通常のHTTP要求の処理と同じである。

https_test_server_2

このプログラムはHTTPSサーバとして一般的に活用できるように必要な機能(以下に示す)を含めたものである。読者はこのコードを理解することで、実際の商用に耐えるようなサーバが開発できるようになろう。

  • favicon.icoに対応 (これは下図のブラウザのタブに表示されるアイコンである)

  • ブラウザ画面に自分が用意したグラフィックスを表示できるようにファイル・サーバ機能を含めている

  • セッション管理とそれを使った画面遷移の基本的な手順




このプログラムはセッション管理の学習に使ったHttpSessionTestServer.dartと類似しているので、簡単に実験出来よう。

favicon.icohttp://www.favicon.cc/のようなツールを使って簡単に作成できる。このアプリケーションでは/resoucesというディレクトリに他の静的リソースとともに収容してある。従ってクライアントからのfavicon要求に対しては、そのディレクトリに変換してファイル・サーバ機能を呼び出している。



関連APIの和訳

HttpServer.bindSecure

注意:ここではstaticメソッドのbindSecureのみを抜き出してある。

HttpServer class

実装

Socket

Stream<HttpRequest>

staticメソッド

Future<HttpServer> bindSecure(

address,

int port,

SecurityContext context,

{int backlog: 0,

bool v6Only: false,

String certificateName,

bool requestClientCertificate: false,

bool shared: false}

)

addressString またはInternetAddressのいずれかであり得る。もしaddressStringのときは、bindInternetAddress.lookupを実行し、そのリストの最初の値を採用する。ローカル・ホストからの到来接続のみを受け付けるループバック・アダプタ上でリスンするときは、この値としてInternetAddress.LOOPBACK_IP_V4またはInternetAddress.LOOPBACK_IP_V6 を使用すること。

ネットワークからの到来接続を受け付けるときは、総てのインターフェイスにバインドするためにInternetAddress.ANY_IP_V4またはInternetAddress.ANY_IP_V6という値のどれかを使用するか、特定のインターフェイスのIPアドレスを使用する。

もしIPバージョン6(IPv6)アドレスが使われているときは、IPバージョン6(IPv6)IPバージョン4(IPv4)の接続の双方とも受け付けられる。IPバージョン6(IPv6)アドレスのみに制限したいときは、IPバージョン6のみとセットするために v6Onlyを使用する。

もしport0という値であるときは、このシステムではエフェメラル・ポートがこのシステムで選択される。実際に使われているポート番号はportゲッタで取得できる。

オプショナルな引数である backlogは下位に存在しているOSのリスン設定に対しリスン・バックログを指定するのに使われる。backlog0(デフォルト値)という値を持っているときは、このシステムで妥当な値が選択される。

ニックネームまたは識別名(DN: Distinguished Name)をもった証明書の certificateNameは証明書データベース内で検索され、サーバ認証用に使用される。もし requestClientCertificatetrueのときは、そのサーバはクライアントたちにクライアント証明書で認証するよう要求する。

オプショナルな引数であるsharedは同じaddress, port 及びv6Onlyの組み合わせにたいし、同じDartのプロセスからの追加的なバインドを可能とするかどうかを指定する。もしsharedtrueのときは、付加的なバインドが実行され、到来接続はHttpServerたちのセット間で分配される。これを使う一つの手段としては、複数のアイソレートたちに到来接続たちを配分させることである。

SecurityContext

SecurityContext class

安全なクライアント接続を確立する際に信頼するための証明書、安全なサーバからサービスするための証明書チェインとプライベート鍵で構成されるオブジェクト。

SecureSocket及びSecureServerクラスは各々のconnectおよびbindメソッドたちへの引数としてあるSecurityContextオブジェクトをとる。

証明書たちと鍵たちはディスク上のPEMファイル達からSecurityContextオブジェクトに付加できる。PEMファイルはひとつまたはそれ以上の"-----BEGIN CERTIFICATE -----""-----END CERTIFICATE-----"といったデリミタ文字列で囲まれたbase-64エンコードの DER直列化 ASN1オブジェクトたちで構成される。DER(Distinguished Encoding Rules)ASN1オブジェクトたちをバイト文字列へのカノニカルなバイナリ直列化である。

static属性

SecurityContext defaultContext

リード・オンリー

コンストラクタ

SecurityContext()


メソッド

void setAlpnProtocols(

List<String> protocols,

bool isServer

)

あるクライアント接続またはサーバ接続でサポートされるアプリケーション・レベルのプロトコルのリストをセットする。TLSに対するこのALPN (application level protocol negotiation)拡張により、クライアントはTLSクライアントhelloメッセージのなかでプロトコルのリストを送信でき、サーバはそのなかから一つを選択しそれをサーバのhelloメッセージのなかで送り返すことができるようになる。

同じSecurityContextを使ってクライアント接続またはサーバ接続に対し別のプロトコルたちのリストを送信できる。ブール値のisServer引数は、このリストをサーバ接続のためにセットするかあるいはクライアント接続のためにセットするかを指定する。

void setClientAuthorities(

String file

)

接続しようとしているクライアントからクライアント認証を要求する際にSecureServerが受け付けたと広告する認証局の名前のリストをセットする。fileは受け付けられた認証局の署名つきの証明書(認証局名は証明書から抽出される)を含んだPEMファイルである。

void setTrustedCertificates(

{String file,

String directory}

)

SecureSocketクライアント接続があるセキュアなサーバに接続する際に使われる信頼される X509証明書たちのセットをセットする。

信頼される証明書たちのセットをセットするには、証明書たちのために単一のPEMファイルで、あるいは個々のPEMファイルたちを含むディレクトリでセットする2つの手段がある。

fileX509証明書たちを含むオプショナルなPEMファイルであり、通常認証局たちからのルート証明書たちである。

directoryPEMファイルたちを含むオプショナルなディレクトリである。このdirectoryは付加されたファイル・システムのリンクが含まれていなければならない。このリンクたちはその証明書を含むファイルへの証明書の識別名(distinguished nameDN)のハッシュに基づいたファイル名たちにリンクする。

OpenSSLにはあるディレクトリ内でこれらのリンクたちを生成するためのc_rehashというツールがある。

void useCertificateChain(

String file

)

セキュアな接続を作るためにSecureServerによってサービスされるX509証明書たち(サーバ証明書が含まれる)のチェインをセットする。fileX509証明書たちが含まれるPEMファイルであり、そのサーバ証明に対する署名されたチェインを構成するルート局及び中間局たちで始まり、そのサーバ証明書で終了する。このサーバ認証のためのプライベート鍵は usePrivateKeyでセットされる。

void usePrivateKey(

String keyFile,

{String password}

)

あるサーバ証明書またはクライアント証明書のためのプライベート鍵をセットする。この SecurityContextを使うセキュアな接続は、署名及びメッセージの復号のためにサーバまたはクライアント証明書を持ったこの鍵を使用する。 keyFileはパスワードで暗号化された暗号化されたプライベート鍵を含むPEMファイルである。暗号化されていないファイルも使用可能であるが、それは一般的でない。





前のページへ

次のページ