目次


OpenSSL API によるセキュア・プログラミング

第 3 回 セキュアなサービスを提供する

OpenSSL で必要な機能を追加する

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: OpenSSL API によるセキュア・プログラミング

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:OpenSSL API によるセキュア・プログラミング

このシリーズの続きに乞うご期待。

このシリーズではこれまで、OpenSSL を使用してクライアント側のアプリケーションを作成する方法について説明してきました。第 1 回では、OpenSSL による基本的なセキュア・クライアントの作成を説明し、第 2 回ではデジタル証明書を詳細に取り上げました。これらの記事に関する読者からの E メールや肯定的な意見を受け取ってみて明らかになったのは、次はサーバーについて論理的に検討しなければならないということです。

サーバーはネットワーク、そしてファイルやデバイスなどのリソースにアクセスできるインターネットを提供します。これらのサービスは場合によっては、セキュリティーで保護されたチャネルで提供しなければなりません。OpenSSL では、セキュア・チャネルとオープン・チャネルの両方を使用したサービスを作成できます。

OpenSSL で基本サーバー・アプリケーションを作成する方法は、本質的には基本クライアント・アプリケーションを作成する方法とほとんど同じです。その違いは比較的少なく、そのうちの一つとしてあげるとしたら当然、サーバーは出接続を作成する代わりに、入接続を受け入れるようにセットアップされるということです。また、第 2 回のデジタル証明書で説明したように、サーバーの場合は、ハンドシェーク中に使用するセキュリティー証明書も提供しなければなりません。

根気良く待つという役割

サーバーはほとんど、入接続をじっと待っているだけです。結局のところ、それがサーバーの役割なのです。Web サーバーはブラウザーがページを要求するのを待ち、FTP サーバーはクライアントがファイルを要求するのを待ち、そしてチャット・サーバーはチャット・クライアントからの入接続を待ちます。これらのサーバーは、ただ待つだけです。

セキュアなクライアント通信とサーバー通信とでは、ハンドシェークに関してはまったく正反対であるという点を除き、ほとんど変わりはありません。その他についてはすべて同じです。

つまり、OpenSSL でクライアント・アプリケーションを作成する方法がわかっていれば、OpenSSL でセキュアなサーバー・アプリケーションを作成するのは至って簡単です。OpenSSL を使用したクライアント・アプリケーションの作成方法がわからない場合は、このシリーズの第 1 回「API の概要」を読んで、OpenSSL ライブラリーのセットアップ方法を学んでください。

識別の 2 つの形式

あるいは、識別に至る 2 つの部分と言ったほうが適切かもしれません。

サーバーの役割は、ハンドシェーク中に使用されるセキュリティー証明書を提供することです。完全なサーバー証明書は、公開鍵と秘密鍵という 2 つの部分で構成されています。公開鍵はクライアントに送信される一方、秘密鍵は秘密にされます。

信頼証明書がクライアント・アプリケーションのライブラリーに提供されなければならないのと同様に、サーバーの鍵はサーバー・アプリケーションのライブラリーに提供されなければなりません。これを提供する関数はいくつかあります。

リスト 1. サーバー証明書をロードする関数
SSL_CTX_use_certificate(SSL_CTX *, X509 *)
SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d);
SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);

この関数の ASN1 バージョンは、ASN1 でエンコードされたデジタル証明書を、指定されたメモリー・ロケーションから SSL コンテキストにロードします。最初の関数は、特定のメモリー構造で提供された X.509 証明書をロードし、最後の関数 (_file の関数) は、PEM でエンコードされたデジタル証明書をファイルからロードします。この関数の type パラメーターによって、DER でエンコードされた証明書をロードすることが可能になります。

秘密鍵をロードするには、以下の関数のいずれかを使用します。

リスト 2. 秘密鍵をロードする関数
SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey);
SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d, long len);
SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa);
SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len);
SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type);

必要となる操作

いずれの秘密鍵も、暗号化して保管するのが最善です。ですが問題は、証明書をロードする関数は、暗号化された証明書のパスワードを要求しないということです。OpenSSL はこれに代わって、パスワードを取得するためのコールバック・メカニズムを提供します。

コールバックのフォーマットは以下のとおりです。

リスト 3. コールバック・フォーマット
int password_callback(char *buf, int size, int rwflag, void *userdata);

この記事では、最後のパラメーター userdata は必要ありません。バッファーはこの関数が呼び出される前に割り振られるため、バッファーのサイズは制御できないためです。

パラメーター rwflag は読み取り/書き込みフラグです。このパラメーターを使用して、パスワードを使用して情報を暗号化するか (rwflag = 1)、または暗号化解除するか (rwflag = 0) をプログラム的に決定できます。コールバックを使用してデータを暗号化するためのパスワードを要求する場合は、パスワードを何らかの方法で 2 回要求して、タイプミスを検出するようにしてください。

パスワードは証明書をロードするときに、暗号化解除してメモリー内で保管できるようにするため、一度だけ要求されます。ユーザーからパスワードを取得する方法は、完全に実装次第です。

パスワードのコールバック関数を作成したら、以下のように SSL_CTX_set_default_passwd_cb を使用して SSL コンテキストにインストールします。

リスト 4. コールバック関数のインストール
/* ctx is a pointer to a previously created SSL context, and cb is the pointer
 * to the callback function you created.
 */

SSL_CTX_set_default_passwd_cb(ctx, cb);

鍵を差し込む

ユーザーにパスワードを求めるコールバック関数が作成されると、実際に証明書をインポートする関数を使用できるようになります。証明書は、既存のメモリー構造またはファイルからインポートできます。

一般的なデジタル証明書の処理方法 (Apache HTTP Server Project での処理方法など) により近づけるため、ここではファイルから証明書をロードする方法を説明します。証明書をロードする方法は、このシリーズの第 1 回で説明した例で証明書ストアをロードした方法と同様です。

まず、クライアントに送信される公開証明書は以下のようにロードします。

リスト 5. 公開証明書のロード
/**
 * ctx is the SSL context created earlier
 */

if(SSL_CTX_use_certificate_file(ctx, "/path/to/certificate.pem", SSL_FILETYPE_PEM) < 1)
{
    /* Handle failed load here */
}

公開証明書をロードしたら、次に秘密証明書をロードします。クライアントは情報を公開証明書に暗号化してサーバーに送信するため、この部分は、ハンドシェーク中に必要になります。このデータは、秘密鍵によってのみ暗号化解除できます。説明の一貫性を保つため、ここでもファイルから鍵をロードします。

リスト 6. 秘密鍵のロード
if(SSL_CTX_use_PrivateKey_file(ctx, "/path/to/private.key", SSL_FILETYPE_PEM) < 1)
{
    /* Handle failed load here */
}

セットアップの仕上げ

コンテキストをセットアップして (上記の補足記事「SSL コンテキスト」を参照)、鍵をロードしたら、今度はセットアップを完了するために BIO オブジェクトを作成します。第 1 回で、SSL 通信と非 SSL 通信の両方を OpenSSL の BIO ライブラリーを使用して確立した方法を覚えていますか。第 1 回の記事に合わせて、ここでも同じことを実行します。

リスト 7. BIO ポインター
BIO *bio, *abio, *out;

BIO オブジェクトが 3 つありますが、そんなに必要なわけはないと疑っていませんか。私を信じてください。3 つのそれぞれには目的があります (信頼とセキュリティーは相伴うものです)。

最初の bio は、SSL コンテキストから作成されるメインの BIO オブジェクトです。2 番目のabio は、BIO 許可オブジェクトで、入接続を許可するためのものです。3 番目の BIO である out は、サーバーがクライアントと対話するために使用するものです。

リスト 8. メイン BIO オブジェクトのセットアップ
bio = BIO_new_ssl(ctx, 0);
if(bio == NULL)
{
    /* Handle failure here */
}

/* Here, ssl is an SSL* (see Part 1) */

BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

上記の BIO オブジェクトのセットアップは、クライアント接続の場合とは多少異なります。第 1 回の記事を思い出せば分かるように、クライアント接続のセットアップには BIO_new_ssl_connect を使用します。

ここでは、SSL_CTX オブジェクトへのポインター、そしてフラグという 2 つのパラメーターを持つ BIO_new_ssl を使用してセットアップしています。このフラグは、作成する BIO オブジェクトの種類を OpenSSL に指示するもので、サーバーの場合は 0、クライアントの場合は 1 となります。上記のコードではクライアント接続をセットアップしようとしているため、フラグは 0 に設定されています。

リスト 9. BIO 許可のセットアップ
abio = BIO_new_accept("4422");
BIO_set_accept_bios(abio, bio);

BIO_do_connect がクライアント接続のための BIO を作成する一方、BIO_new_accept はサーバー接続のための BIO を作成します。ストリングでエンコードされる引数は 1 つ、つまりリッスンするポートだけです。

セキュアな接続をリッスンすることを目的としているため、セキュアな BIO をこの BIO 許可につなぐ必要があります。そこで、2 番目の関数コール BIO_set_accept_bios の出番です。この関数コールにより、前に作成した SSL BIO が BIO 許可につながれます。

この関数コールによって、SSL BIO を解放する必要もなくなります。SSL BIO は、BIO 許可が破壊されると自動的に解放されるためです。

今度はじっと待つ

サーバーは釣り人のようなもので、クライアントが餌に喰いつくまで、ただじっと待つだけです。サーバーは釣りのように、入接続をひたすら待ち続けます。

Winsock や BSD ソケットを経験したことがあるなら、おそらく accept 関数を思い付くことでしょう。OpenSSL でこれに対応するのは BIO_do_accept です。その違いは、accept は一度呼び出せば待機させられますが、BIO_do_accept は二度呼び出さないと待機しないという点です。

リスト 10. サーバーへの待機指示
/* First call to set up for accepting incoming connections... */

if(BIO_do_accept(abio) <= 0)
{
    /* Handle fail here */
}

/* Second call to actually wait */

if(BIO_do_accept(abio) <= 0)
{
    /* Handle fail here */
}

/* Any other call will cause it to wait automatically */

BIO_do_accept の最初の呼び出しによって、BIO は入接続を受け入れるように設定されます。次の呼び出しは、実際に待機させるために必要となります。その後、サーバーは任意の期間中待機し続けます。

着信呼に応答する

BIO_do_accept は入接続を受信すると 1 を戻します。ただし、BIO 許可だけでは対話できません。OpenSSL は BIO_pop を使用して、BIO 許可からポップする別の BIO を作成します。

リスト 11. 対話するための接続のポップ
out = BIO_pop(abio);

if(BIO_do_handshake(out) <= 0)
{
    /* Handle fail here */
}

BIO 許可から入接続をポップした後は、BIO_do_handshake の呼び出しによってハンドシェークを処理しなければなりません。今までのセクションでのセットアップが成功していれば、ハンドシェークも同じく成功するはずです。

サーバーは BIO ライブラリーが使用可能な各種の読み取り/書き込み関数を使用して、実際にクライアントと対話します。その詳細については、第 1 回を参照してください。

優れたサービスの提供

全体的に見ると、基本的な方法さえ理解できれば、OpenSSL でセキュアなサーバー・アプリケーションを作成するのは難しいことではありません。この記事に記載したコード・サンプルを拡張して、ニーズに合った完全な SSL サーバー・アプリケーションを作成することができます。ただし、あらかじめ注意しておきますが、この記事に記載したコード・サンプルと、以下の「ダウンロード」セクションで入手できるコード・サンプルは最小限のものなので、試験用のみに使用してください。完全な SSL サーバー・アプリケーションを実際に作成する前には必ず、最新のセキュリティー推奨事項を読んで研究してください。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=231745
ArticleTitle=OpenSSL API によるセキュア・プログラミング: 第 3 回 セキュアなサービスを提供する
publish-date=09272006