MASATOの開発日記


前の開発日記 次の開発日記 一覧

2003/06/10

今私が使えるオブジェクト指向プログラミング言語が、C++とJavaとC#。 C++は、速度が必要なアプリケーションを開発するときに使います。 例えばゲームアプリケーションは速度が必要なのでC++&Win32&DirectXで開発しています。 Javaは、他のOSで動かす必要があるアプリケーションを開発するときに使います。 例えば、WebサービスのサーバーアプリケーションはWindowsやLinuxで動かすので、Java&Axisで開発しています。 C#は、その他のアプリケーションを開発するときに使います。 例えば、Webサービスのクライアントや小さなコマンドラインツールなどはC#で開発してます。

どの言語も使い分けが出来ていると言えば出来ているのですが、色々な言語を修得するのは大変です。 どの言語も使えます!と言えるのは良いのですが、どれも中途半端に修得するのは大変です。 今のところ私はどの言語の修得度も中途半端になってしまっています。せめて2つに絞りたいところです。

今一番不要な言語はどれかと言われたらC#です。Javaでほぼ置き換えることができます。 しかし、C#って一番新しい言語なんですよねー。一番洗練されているという印象もあります。 この3つの中からC#を切り捨てるということは、ベテラン2人新人1人の職場から1人免職にするときに新人を選ぶようなもの。 いやまあそれはそれで良いのですが、それって未来に希望が持てません。 じゃあどうしよっかなーーーと悩み中。

DESアルゴリズムを用いてバイト列を暗号化/復号化する方法

環境Visual C#.NET

C#においてDESアルゴリズムを用いてバイト列(byte[])を暗号化/復号化する方法を解説します。 以前、2003/01/13で解説したTransformerクラスを使います。 用意しておいて下さい。

まず、暗号/復号を行うための、初期化ベクタとキーを生成します。 これらは両方とも一定の長さのランダムな値が格納されたバイト列なのですが、 単純にRandomクラスを使ってランダムに生成すると、運悪くWeakキーやSemi-Weakキーを引いてしまうかもしれません。 WeakキーとSemi-Weakキーは、DESアルゴリズムにおいて、「復号化しやすいキー」のことです。 MSDNによると4つのWeakキーと6つのSemi-Weakキーが発見されているらしいです。
DESCryptoServiceProviderクラスが、WeakキーやSemi-Weakキー以外のキーをランダムに生成する方法を用意してくれていますので、 こちらを使いましょう。
初期化ベクタの方にはWeak初期化ベクタという話はないので、ランダム値でも構わないような気もしますが、 DESCryptoServiceProviderクラスがランダムに初期化ベクタを生成する方法を用意しています。 こちらを使って損は無いはずなので、こちらを使いましょう。

初期化ベクタとキーを生成するため、次のコンソールアプリケーションを実行しましょう。 System.Windows.Formsへの参照を追加してコンパイルして下さい。 実行すればクリップボードに初期化ベクタとキーがコピーされていますので、 そのままソースコードにペーストできます。

using System;
using System.Security.Cryptography;
using System.Windows.Forms;

namespace IVKeyGenerator {
  class DESGenerator {
    [STAThread]
    static void Main(string[] args)  {
      string s = Generate(new DESCryptoServiceProvider());
      Console.Write(s);
      Clipboard.SetDataObject(s, true);
    }
    static string Generate(SymmetricAlgorithm algorithm) {
      string s = "byte[] iv = new byte[] {";
      for (int i = 0; i < algorithm.IV.Length; i++){
        if (i > 0) s += ", ";
        s += algorithm.IV[i];
      }
      s += "};\r\n";
      s += "byte[] key = new byte[] {";
      for (int i = 0; i < algorithm.Key.Length; i++){
        if (i > 0) s += ", ";
        s += algorithm.Key[i];
      }
      s += "};\r\n";
      return s;
    }
  }
}

このアプリケーションは、実行するたびに異なる初期化ベクタとキーを生成します。 参考までに、私が実行したところ、次のような初期化ベクタとキーが生成されました。

byte[] iv = new byte[] {242, 7, 191, 252, 124, 89, 183, 115};
byte[] key = new byte[] {157, 192, 212, 17, 133, 224, 7, 149};

さて、次は肝心の暗号化/復号化部分です。 暗号化/復号化を行うクラス(名前はCryptorとします)は以下のようになります。 ここでようやくTransformerクラスを使います。

using System.Security.Cryptography;

  class Cryptor {
    static private SymmetricAlgorithm algorithm;
    static private void InitAlgorithm() {
      if (algorithm == null) {
        algorithm = new DESCryptoServiceProvider();
        algorithm.IV = new byte[] {242, 7, 191, 252, 124, 89, 183, 115};
        algorithm.Key = new byte[] {157, 192, 212, 17, 133, 224, 7, 149};
      }
    }
    static public byte[] Encrypt(byte[] data) {
      InitAlgorithm();
      return new Transformer(algorithm.CreateEncryptor()).Transform(data);
    }
    static public byte[] Decrypt(byte[] data) {
      InitAlgorithm();
      return new Transformer(algorithm.CreateDecryptor()).Transform(data);
    }
  }

IVとKeyに代入する値は、先ほどのコンソールアプリケーションが生成した値に置き換えておきましょう。

暗号化用のメソッドがEncryptで、復号化用のメソッドがDecryptです。 共に、バイト列を与えると暗号化/復号化して結果をバイト列で返すメソッドです。 なお、Decryptメソッドは、復号に失敗するとCryptographicExceptionを返すことがあります。 catchして、復号失敗時の処理を行いましょう。

このクラスCryptorを使って、重要なデータのファイルへの書き込み前に暗号化、読み込み後に復号化することによって、 重要なデータを盗み見られることを一応防ぐことができます。
一応というのは、この方式には次のような問題があるからです。

というわけで、この方法ではいわゆるカジュアルコピーが防止できるだけです。 その力の限界を見極めた上で使いましょう。

前の開発日記 次の開発日記 一覧