ファイヤープロジェクト
OpenSSLで3DESしてみる
2007-05-02T14:30+09:00   matsu
OpenSSLにはDES暗号化する関数が提供されている.これにはいろいろなバリエーションがあるようだが,良くわからないので,とりあえずメジャーそうな3DESを試してみる.
OpenSSLでDES暗号化するには,大まかに以下の手順となる.
キースケジュールの生成
DESはキーからサブキーを生成して,このサブキーを使用して暗号化する. で,ライブラリではこのサブキーのセット(16個)をキースケジュールと呼んでいて,これを生成する必要がある.
暗号化
生成したキースケジュールを使用して実際の暗号化処理を行う. 今回はDESを3重に行う3DESを試してみる. OpenSSLでは3DESの関数は暗号化と復号で共通になっていて,フラグで制御する.
OpenSSLのDES処理では,二つの中心的なデータ型がある.
DES_cblock
キー,暗号化対象データ,復号データ,暗号文データを格納するデータ型. des.hでは
typedef unsigned char DES_cblock[8];
とされている. DESでは処理の際にデータを扱う単位が8バイトとなっており,これを保証する意味あいがあるのだと思う. 暗号化対象データが8バイトで割り切れない場合は,ヌルで埋めるなどの処理をしておく必要がある.
DES_key_schedule
キースケジュールを格納するデータ型. 内容は(DES_cblock+α)が16個あり,これをサブキーとしてラウンドロビンで実際の暗号化/復号処理のキーとして使用する. +αが何なのかはよくわからない.
では,サンプル.
第一引数を暗号化対象データとし,第二〜第四引数を暗号化キーとして3DESで暗号化,復号し,その前後のデータを表示する. 第二〜第四引数が足りない場合は,ランダムでキーを生成する. サンプルでは,まずキーを引数から受け取り,そこにチェックパリティをセットしている.
  // キー情報作成
  memset(key, 0, KEY_NUM * sizeof(DES_cblock));
  for (i = 2; i < argc; i++)
    {
      sscanf(argv[i], "%8s", (char *)&key[i - 2]);
    }
  for (; i < KEY_NUM + 2; i++)
    {
      DES_random_key(&key[i - 2]);
    }
  for (i = 0; i < KEY_NUM; i++)
    {
      DES_set_odd_parity(&key[i]);
    }
引数でたりない分のキーをランダム生成するために,OpenSSLにて提供されている関数を使用している.
void
DES_random_key(DES_cblock *ret);
引数には生成したランダムキーデータを格納する領域のポインタを渡す. キーデータの8バイトにはパリティビットが含まれており,それを計算してセットするのは面倒というか,よくわからないので,OpenSSLにて提供されている関数を使用してパリティをセットする.
void
DES_set_odd_parity(DES_cblock *key);
引数にはパリティをセットしたいキー情報領域のポインタを渡す. これにより,このサンプルでは引数のキーを使用するのではなく,パリティを考慮していないキーにパリティをセットしてこれを実際のキーとして扱う. 次に準備したキーデータからキースケジュールを生成する.
  // キースケジュール作成
  DES_key_schedule schedule[KEY_NUM];
  for (i = 0; i < KEY_NUM; i++)
    {
      ret = DES_set_key_checked(&key[i], &schedule[i]);
      if (ret != 0)
	{
	  fprintf(stderr, "DES_key_set_checked 失敗 ");
	  if (ret == -1)
	    {
	      fprintf(stderr, "パリティ不正\n");
	    }
	  else if (ret == -2)
	    {
	      fprintf(stderr, "脆弱なキー\n");
	    }
	  else
	    {
	      fprintf(stderr, "不明なエラー\n");
	    }
	  exit(-1);
	}
    }
キースケジュールの作成もOpenSSL提供の関数を使用しており,キーに問題がなければうまく生成できる.
int
DES_set_key(const_DES_cblock *key,
            DES_key_schedule *schedule);

int
DES_set_key_checked(const_DES_cblock *key,
                    DES_key_schedule *schedule);

void
DES_set_key_unchecked(const_DES_cblock *key,
                      DES_key_schedule *schedule);

いずれも第一引数にキー,第二引数に生成したキースケジュールを格納する領域のポインタを渡す. DES_set_key_checkedは処理に先だってキーのパリティが正当か,キーが脆弱でないかをチェックする. 返り値が-1ならパリティ不正,-2ならキーが脆弱である. DES_set_key_uncheckedはチェックなしでキースケジュールを生成する. DES_set_keyはグローバル変数のDES_check_keyが0でなければ,DES_set_key_checkedと等価,0ならばDES_set_key_uncheckedと等価となる. ただし,man desによるとグローバル変数に依存するこの関数よりも,前者二つを使用する方が推奨されるらしい.
暗号化と復号処理は,準備したキースケジュールで関数を呼び出すだけである.
  // 暗号化
  DES_ecb3_encrypt(&rawData, &cryptData,
		   &schedule[0], &schedule[1], &schedule[2],
		   DES_ENCRYPT);

  // 復号
  DES_ecb3_encrypt(&cryptData, &decryptData,
		   &schedule[0], &schedule[1], &schedule[2],
		   DES_DECRYPT);
OpenSSLでは暗号化,復号処理の関数がいろいろあってよくわからないが,今回は3DESにしてみた.
void
DES_ecb3_encrypt(const_DES_cblock *input, DES_cblock *output,
                 DES_key_schedule *ks1, DES_key_schedule *ks2,
                 DES_key_schedule *ks3, int enc);
引数の意味は以下の通り.
第一引数
入力(暗号化の際は平文,復号の際は暗号文)
第二引数
出力(実際の意味は第一引数と逆)
第三〜第四引数
キースケジュール 一つの暗号化鍵から生成したキースケジュールだと,普通のDESと同じで脆弱なので(3DESの意味がない)注意する.
第五引数
暗号化,復号の制御フラグである. DES_ENCRYPTが暗号化,DES_DECRYPTが復号.
コンパイルする際には,リンカオプションを設定する(※).
LDFLAGS=-lssl
では実行してみる.
$ ./my3des FIREPROJ 12341234 abcdefgh ABCDEFGH
キー情報
KEY [31 32 32 34 31 32 32 34 ]
KEY [61 62 62 64 64 67 67 68 ]
KEY [40 43 43 45 45 46 46 49 ]
暗号化対象データ  [46 49 52 45 50 52 4F 4A ]
暗号文            [FB A3 3C 0B 98 2E 0C 29 ]
復号データ        [46 49 52 45 50 52 4F 4A ]
※ OpenSSLがインストールされていないなら,あらかじめインストールしておく.
apt-get install libssl-dev
matsu(C)
Since 2002
Mail to matsu