ファイルの暗号化

Last Updated 2011/09/21


.NET Framework にはファイルを暗号化するクラスがたくさん用意されています。しかし、使用例はありますが、実用的とはいえません。そこで、暗号化アルゴリズムの一般的な説明と、実用的な使用例について解説します。なお、私は格別、暗号化技術に強いわけではありません。WEB サイトをウロウロして仕入れた知識の寄せ集めであることをお断りしておきます。

なお、暗号化クラスを Component のページで公開しています。一度、試してください。


.NET Framework の暗号化クラスは大きく分けると、次の 2 種類に分類できます。

対称暗号化クラスは、秘密キー(あるいは、共有キーという)を使う暗号化クラスです。つまり、ファイルの送信者と受信者とが第三者に対して秘密にしておかなければならない共有キーを使ってファイルを暗号化または暗号の解読をするものです。したがって、共有キーをパスワードと呼んでもいいかもしれません。

一方、非対称暗号化クラスは、いわゆる、公開キーと秘密キーの両方を使うものです。公開キーはその名が示すように秘密にしておく必要のないキーで、第三者の手に渡っても問題ありません。

非対称暗号化クラスを使う場合の手順を説明します。

  1. ファイルの受信者は公開キーと秘密キーを作成し、公開キーを送信者に送る
  2. 送信者は受け取った公開キーを使ってファイルを暗号化し、暗号化済みのファイルを受信者に送る
  3. ファイルを受け取った受信者は自分で作った秘密キーを使って暗号を解読する

この方式のミソは、公開キーから秘密キーを探り出すことは不可能という点です。ただし、アルゴリズムが複雑ですから、暗号・解読処理時間が大幅にかかります。したがって、大量のデータを送受信する場合に使うべきではありません。実践的には、ファイルの本文は対称暗号化クラスを使って暗号化し、その秘密キーを非対称暗号化クラスを使って受信者に送るといった手順になるでしょう。

対称暗号化クラス

1. DESCryptoServiceProvider クラス

DES "Data Encryption Standard" は 1976 年に策定され、1977 年にアメリカの標準規格として採用されたものです。64 ビットの原文を 64 ビットの暗号文に変換します。キーの長さはパリティビットを含めて 64 ビット(実質 56 ビット)です。現在の暗号化アルゴリズムと比較すると、古さはいなめないので、積極的に使う理由はないと思います。

2. TripleDESCryptoServiceProvider クラス

DES をより強化したもので、64 ビットのキーを 3 つ用意し、暗号化します。暗号を解読する場合はキーを逆順で使用しなければなりません。

3. RC2CryptoServiceProvider クラス

RC2 は Ron Rivest が開発したもので、"RC" の由来は、Ron Code あるいは Rivest's Cipher ということらしいのですがハッキリしません。開発者名から想像できるように、RSA を改良したものです。RC2 以後、RC4、RC5 を発表しているので、"2" はバージョン番号のようなものなのかもしれません。

4. RijndaelManaged クラス

Rijndael は開発者の Joan Daemen と Vincent Rijmen の名前を合成したもので、「ラインダール」と読むらしい。AES "Advanced Encryption Standard" が暗号アルゴリズムを公募して、2000 年に採用され、DES に代わる、アメリカの標準規格となっているそうです。128 ビットの原文を 128 ビットの暗号文に変換します。標準規格としてのキーの長さは 128、192、256 ビットの 3 種類です。

非対称暗号化クラス

1. RSA

開発者の Ron Rivest、Adi Shamir、Leonard Adleman の 3 人の名前に由来する暗号方式で、1977 年に開発され、公開キー暗号方式としてはもっとも普及している方式です。1983年に特許を取得しましたが、現在では特許が切れていますので、自由に利用することができます。

2. DSA "Digital Signature Algorithm"

電子署名専用のアルゴリズムという以上のことはいまのところ不明です。

RC2CryptoServiceProvider クラス

ネットワーク経由でファイルをやり取りするとき、もっとも簡便な方法はパスワードを使う手です。そこで、パスワードからキーとなるバイト配列を作成する PasswordDeriveBytes クラスと共有キーを使う  RC2CryptoServiceProvider クラスとを組み合わせて、実用的なファイルの暗号化・暗号の解読方法を解説します。

次のコードは、フォームに btnEncrypt と btnDecrypt を配置し、btnEncrypt をクリックすると、カレントディレクトリ内にあるファイル a.txt を読み込み、暗号化して、ファイル a.dat に出力します。btnDecrypt をクリックすると、すでに作成した a.dat を読み込み、暗号を解読してファイル a1.txt として出力します。

パスワードの PASSWORD と初期化ベクタの IV は送信者と受信者の両方で共有しなければなりません。また、PasswordDeriveBytes オブジェクトを作成するときの条件も両者で同じでなければなりません。なお、例外発生時の処理はしていませんから、実際に使うコードでは追加してください。

 using System.Security.Cryptography;
 using System.IO;

 // メンバ変数
 private const string PASSWORD = "emanual";
 private byte[] IV = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; // ← 自由に設定してください

 private void btnEncrypt_Click(object sender, EventArgs e)
 {
   PasswordDeriveBytes pdb = new PasswordDeriveBytes(PASSWORD, null);

   byte[] key = pdb.CryptDeriveKey("RC2", "MD5", 0, IV);
   //------------------------------------------------------------
   // 暗号化するファイルを読み込む
   FileStream stream = new FileStream("a.txt", FileMode.Open);

   byte[] buffer = new byte[stream.Length];

   stream.Read(buffer, 0, (int)stream.Length);
   stream.Close();

   // RC2CryptoServiceProvider オブジェクトを作成する
   RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();

   // 暗号化オブジェクトを作成する
   ICryptoTransform encryptor = rc2.CreateEncryptor(key, IV);

   // 暗号化データの出力先のストリームを作成する
   FileStream outStream = new FileStream("a.dat", FileMode.Create);

   // 暗号化ストリームを作成する
  CryptoStream encryptStream = new CryptoStream(outStream, encryptor, CryptoStreamMode.Write);

   // バッファからデータを読み込み、暗号化して出力ストリームに書き出す
   encryptStream.Write(buffer, 0, buffer.Length);

   // 内部バッファの内容をフラッシュしたあと、関連付けたストリームを閉じる
   encryptStream.Close();
 }
 //-----------------------------------------------------------------------------

 private void btnDecrypt_Click(object sender, EventArgs e)
 {
   PasswordDeriveBytes password = new PasswordDeriveBytes(PASSWORD, null);

   byte[] key = password.CryptDeriveKey("RC2", "MD5", 0, IV);
   //--------------------------------------------------------------
   // 暗号化したデータファイルを読み込む
   FileStream stream = new FileStream("a.dat", FileMode.Open);

   byte[] buffer = new byte[stream.Length];

   stream.Read(buffer, 0, (int)stream.Length);
   stream.Close();

   // RC2CryptoServiceProvider オブジェクトを作成する
   RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();

   // 復号化オブジェクトを作成する
   ICryptoTransform decryptor = rc2.CreateDecryptor(key, IV);

   MemoryStream memStream = new MemoryStream(buffer);

   // 復号化ストリームを作成する
  CryptoStream decryptStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read);

   byte[] decryptedBuffer = new byte[buffer.Length];

   // ストリームからデータを読み込んで復号化し、バッファに書き出す
   int count = decryptStream.Read(decryptedBuffer, 0, decryptedBuffer.Length);

   // 内部バッファの内容をフラッシュしたあと、ストリームを閉じる
   decryptStream.Close();

   // 復号化データの出力先のストリーム
   FileStream outStream = new FileStream("a1.txt", FileMode.Create);

   // 復号化したデータを保持するバッファからストリームに書き出す
   outStream.Write(decryptedBuffer, 0, count);

   outStream.Close();
 }

−以上−