WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

厳密な名前付きアセンブリ

by WebSurfer 2011年9月13日 22:37

サンデープログラマーの自分が、作成したアセンブリに厳密名を付けることはないですが、わけあって厳密名の付け方についていろいろ調べましたので、備忘録として書いておきます。

まず、厳密名 (Strong Name) とは何かですが、MSDN ライブラリ 厳密な名前付きアセンブリ によると「単純テキスト名(アセンブリ名)、バージョン番号、カルチャ情報から成るアセンブリの識別子と、公開キーおよびデジタル署名」だそうです。

web.config で、ライブラリへの参照や HTTP モジュールの参照を設定する際以下のようにしますが、これが厳密名を指定したことになるようです。

<add 
  assembly="System.Core, 
  Version=3.5.0.0, 
  Culture=neutral, 
  PublicKeyToken=B77A5C561934E089"/>

<add 
  name="ScriptModule" 
  type="System.Web.Handlers.ScriptModule, 
    System.Web.Extensions, 
    Version=3.5.0.0, 
    Culture=neutral, 
    PublicKeyToken=31BF3856AD364E35"/>

厳密な名前を付ける理由や、それが推奨されるケース/されないケースについては MSDN ライブラリの マネージ アプリケーションに対する厳密な名前による署名 に書いてあるのでそちらを参照してください。手抜きですみません。(笑)

厳密名のうち、アセンブリ名、バージョン番号、カルチャ情報は開発者が自分で設定する(というより、デフォルトで設定されている)ものです。下の画像にそれらが示されています。

Visual Studio のアセンブリ情報の画面

厳密名のなかで、公開キーとデジタル署名、および公開キートークン (PublicKeyToken) が具体的に何かよく分かってなかったので調べてみました。

アセンブリにデジタル署名するには、まず、秘密キー/公開キーペアから成るキーファイルを作成します。次に、アセンブリの一部分のハッシュを作成し、それを秘密キーで暗号化し、公開キーとともにアセンブリに追加(署名)します。

アセンブリをロードする際、アセンブリに含まれる公開キーと秘密キーで暗号化されたハッシュを取り出し、暗号化されたハッシュを公開キーで復号します。次に、ロードするアセンブリ自体のハッシュを作成して、それと復号したハッシュとを比較して一致することを確認します。これにより正しいアセンブリがロードされることを保障できます。加えて、改ざんされていないかどうかもチェックできますので、セキュリティも向上するということのようです。

さて、キーファイルの作成とアセンブリの署名をどのように行うかですが、Visual Studio 2008, 2010(未確認ですが、たぶん 2005 も)には、プロパティページに[署名]タブがありますので、それでキーペアの作成が可能です。キーペアを作成した上でビルドすればアセンブリに署名できます。下の画像を参照してください。ちなみに以前(たぶん Visual Studio 2003 以前)は Sn.exe (厳密名ツール)Al.exe (アセンブリ リンカー) を使って手動で行っていたようです。

Visual Studio の厳密な名前キーの作成画面

上の画面で[アセンブリの署名]チェックボックスにチェックを入れ、[厳密な名前のキー ファイルを選択してください]ボックスの一覧の[<新規作成...>」を選択すると[厳密な名前キーの作成]ダイアログボックスが表示されます。

[厳密な名前キーの作成]ダイアログボックスで、新しいキーファイルの名前を入力し、さらに、必要に応じてパスワードを入力します。パスワードを指定すると、Personal Information Exchange (.pfx) ファイルが作成され、指定しないと Strong Name Key (.snk) ファイルが作成されます。[OK]をクリックすると、以下の画像のようにキーファイルが作成されます。

Visual Studio で作成したキーファイル

.pfx ファイルと .snk ファイルの中身が具体的にどのように違うかは調べていませんが、MSDN ライブラリ [キー ファイルのインポート] ダイアログ ボックス によると、キーファイルをインポートするには .pfk ファイル形式にする必要があるそうです。

アセンブリが正しくロードされるという保障も、改ざんが防止できるというセキュリティ機能も、キーファイルに含まれる秘密キーをアセンブリの提供者(ソフトウェア会社)のみが持っているという前提の上に成り立っています。従って、このキーファイルはソフトウェア会社に固有となるようにし、流出しないよう厳重に管理する必要があるそうです。

上の画像のように、キーファイルを作成してプロジェクトに配置した後は、ビルドすれば自動的にキーファイルに含まれる公開キーと秘密キーを使ってアセンブリに署名されます。

そのようにして厳密名を付けたアセンブリを参照する例を書きます。例えば HTTP モジュールを dll として bin フォルダに配置する場合を考えます。web.config でアセンブリ名、バージョン番号、カルチャ情報、公開キートークンを以下のように設定します

<add 
  name="HelloWorldModule" 
  type="MyHttpModule.HelloWorldModule, 
    MyHttpModule, 
    Version=1.0.0.0, 
    Culture=neutral, 
    PublicKeyToken=90cefab791a0b403"/>

アセンブリ名、バージョン番号、カルチャ情報はすぐ分かりますが、問題は公開キートークン(PublicKeyToken)です。公開キートークンとは、公開キーにハッシュをかけて抽出した値の最後の 8 バイトです。公開キーのサイズは 128 バイトもあるので、それをそのまま参照に使うと、参照しているアセンブリが 1 つ増えるたびに 128 バイトずつサイズが増えてしまうことになります。それではサイズが増えすぎて困るので、そのようにしているらしいです。

公開キートークンを取得するには Sn.exe (厳密名ツール) を利用します。コマンドラインから -T オプションを使用して以下のようにします。

sn.exe を使用して公開キートークンを取得

このようにして web.config で厳密名(アセンブリ名、バージョン番号、カルチャ情報、公開キートークン)を指定すると、アセンブリをロードする際それらの 項目をチェックし、矛盾があると実行時に以下の画像ようなエラーが出ます。

厳密名が異なる場合のエラー画面

最後に、Visual Studio のプロパティページの[署名]タブの[遅延署名のみ(Y)]について簡単に書きます。

これは組織が厳重に保護している(すなわち、一般の開発者にはアクセスが許可されていない)キーファイル(特に秘密キー)を使用しなくても、開発に支障がないようにするためのオプションです。

秘密キーがないと署名(アセンブリのハッシュを取って、それを秘密キーで暗号化した情報のアセンブリへの埋め込み)ができません。それでは開発に支障が出るので、アセンブリに署名をせずに公開キーだけを埋め込み、一時的に署名の検証を無効にして開発を進められるようにします。開発が完了した後、秘密キーにアクセスできる限られた者が、秘密キーを使って署名してリリースします。後で署名するので「遅延署名 (Delay Sign)」と言うらしいです。

詳しくは、第2回 アセンブリのアイデンティティ が参考になると思います。

Tags:

.NET Framework

SmtpClient でメール送信

by WebSurfer 2010年9月7日 14:25

ユーザー登録の際のパスワードの連絡、ブログにコメントが書き込まれた時の通知などのために、Web アプリケーションにメールの自動送信機能を実装するケースは多いと思います。

自分の HP にも、メールの自動送信の機能を実装しています。もっとも、ユーザー登録するような人はいないし、コメントを書き込む人もいないので、ほとんど役に立っていませんが。(苦笑)

メール送信に利用しているのは .NET Framework のライブラリ SmtpClient クラスです。これを利用すると簡単にメール送信ができます。

ただし、日本語対応に問題があり注意が必要です。以下に、文字コードとしてデフォルトの UTF-8 を使った場合と、日本で広く使われている iso-2022-jp (JIS) を使った場合の注意事項を書いておきます。

(1) UTF-8 を使う場合

UTF-8 の場合は、ほとんど文字化けに悩むことはありませんが、アドレスの表示名に日本語を使う場合のみ注意が必要です。

表示名は MailAddress コンストラクタの第 2 引数で設定しますが、日本語の文字列をそのまま代入すると Q エンコードされてしまいます。

Q エンコードで問題なければいいのですが、受信するメーラによっては B エンコードでないと文字化けするかもしれません。(Outlook Express, Windows メールは問題ありませんでしたが)

B エンコードにするには、以下のサンプルコードにある EncodeMailHeader ようなメソッドを作って日本語の表示名を変換し、それを第 2 引数に代入してください。

件名、本文、添付ファイル名は、以下のサンプルのように日本語をそのまま代入して問題ありません。件名、添付ファイル名は、自動的に UTF-8 の B エンコードに変換されて、ヘッダーに設定されます。本文は、UTF-8 の base64 エンコードとなります。

private string EncodeMailHeader(string str, Encoding enc)
{
  string ret = 
    System.Convert.ToBase64String(enc.GetBytes(str));
  ret = string.Format("=?{0}?B?{1}?=", enc.BodyName, ret);
  return ret;
} 

Encoding enc = Encoding.GetEncoding("utf-8");

MailAddress to = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));
MailAddress From = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));

MailMessage mailMessage = new MailMessage(from, to);

mailMessage.Subject = "日本語の件名";
mailMessage.Body = "日本語の本文";

Attachment data = 
  new Attachment("添付ファイル名", 
    System.Net.Mime.MediaTypeNames.Application.Octet);
ContentDisposition disposition = data.ContentDisposition;
disposition.FileName = 
  EncodeMailHeader(System.IO.Path.GetFileName(file), enc);
disposition.CreationDate = 
  System.IO.File.GetCreationTime(file);
disposition.ModificationDate = 
  System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = 
  System.IO.File.GetLastAccessTime(file);

mailMessage.Attachments.Add(data);

SmtpClient smtpClient = new SmtpClient();
smtpClient.Host = "mail.abc.co.jp";
smtpClient.Port = 25;
smtpClient.Send(mailMessage);

(2) iso-2022-jp を使う場合

Encoding を iso-2022-jp にする以外は、メールアドレスを設定するところまでは上記の UTF-8 の場合と同じです。

件名は、直接 Subject プロパティに日本語を代入すると Q エンコードになってしまいます。以下のサンプルのように、EncodeMailHeader メソッドを使って変換してから Subject プロパティに代入しなければなりません。(注:.NET Framework 4.5 で件名のエンコードが変わったそうです。詳しくは下の 2013/5/10 追記を見てください)

一番の問題は本文で、Body を使用すると Content-Transfer-Encoding は 8bit エンコードになってしまいます。これを 7bit にする方法はないそうです。代わりに、AlternateView を使って 7bit エンコードにする方法があります。以下にそのサンプルを書いておきます。

添付ファイル名は、Content-Type の name は、どのよう設定にしても UTF-8 の B エンコードになってしまいます。一方、Content-Disposition の filename は、以下のように設定すれば、iso-2022-jp の B エンコードになります。

以上のように、iso-2022-jp を使うのは、特に本文を AlternateView にするところが、かなり無理やりっぽい感じです。自分的には決してお勧めではないです。

Encoding enc = Encoding.GetEncoding("iso-2022-jp");

private string EncodeMailHeader(string str, Encoding enc)
{
  string ret = 
    System.Convert.ToBase64String(enc.GetBytes(str));
  ret = string.Format("=?{0}?B?{1}?=", enc.BodyName, ret);
  return ret;
}

MailAddress to = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));
MailAddress From = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));

MailMessage mailMessage = new MailMessage(from, to);

mailMessage.Subject = EncodeMailHeader("日本語の件名", enc);

// プレーンテキストのAlternateViewを作成
AlternateView htmlView = 
  AlternateView.CreateAlternateViewFromString(
    "日本語の本文",
    enc,
    System.Net.Mime.MediaTypeNames.Text.Plain);

// TransferEncoding.SevenBitを指定
htmlView.TransferEncoding = 
  System.Net.Mime.TransferEncoding.SevenBit;

mailMessage.AlternateViews.Add(htmlView);

Attachment data = 
  new Attachment("添付ファイル名", 
    System.Net.Mime.MediaTypeNames.Application.Octet);
ContentDisposition disposition = data.ContentDisposition;
disposition.FileName = 
  EncodeMailHeader(System.IO.Path.GetFileName(file), enc);
disposition.CreationDate = 
  System.IO.File.GetCreationTime(file);
disposition.ModificationDate = 
  System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = 
  System.IO.File.GetLastAccessTime(file);

mailMessage.Attachments.Add(data);

SmtpClient smtpClient = new SmtpClient();
smtpClient.Host = "mail.abc.co.jp";
smtpClient.Port = 25;
smtpClient.Send(mailMessage);

ということで、結局自分はどうしているかというと、デフォルトの UTF-8 を使っています。UTF-8 が使えないメーラーなんか相手にしない・・・と言いたいところですが、自分宛のメール送信ぐらいにしか使ってないから、何だっていいのです。(笑)

----- 2013/5/10 追記 -----

.NET Framework 4.5 では件名 (Subject) の実装が変わって、上記の「(2) iso-2022-jp を使う場合」に書いたサンプルコードでは Q エンコードになってしまうそうです。その理由など、詳しくは以下のページを参照してください。

.NET Framework 4.5 の System.Net.Mail で日本語の件名を ISO-2022-JP の Base64 でエンコードして送信する方法

二重にエンコードすることにより B エンコードになるそうです。検証していませんが、上記のサンプルコードでは以下のように変更すればよいはずです。

.NET Framework 4 まで(上記サンプルコードの通り)

mailMessage.Subject = EncodeMailHeader("日本語の件名", enc);

.NET Framework 4.5 では二重エンコード

mailMessage.Subject = 
  EncodeMailHeader(EncodeMailHeader("日本語の件名", enc), enc);

Tags: , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar