今回では文字列をより安全に保存したり、インターネットで転送したりするための簡単暗号化アルゴリズムを紹介いたします。
発想
Base64は8ビットエンコーディングを6ビットエンコーディングに変換するアルゴリズムです。Base64ではデータを生成する際にプレーンテキストのプロトコルでもデータの転送可能のため、プレーンテキストのみを利用しデータの文字列を生成しています。その利用可能なアルファベットはBase64のエンコーダーとデコーダーに事前定義されています。WikipediaのBase64の「変換表」のところをご参照ください。
ネコ技術は6ビットエンコーディングを5ビットエンコーディングに変更し、内蔵のアルファベットの変換表もBase64標準の変換表以外の内容に変更します。この方法で自分だけのプログラムしか認識できない暗号化アルゴリズムを作成します。
クラス構成
internal class CodecToolkit
{
  // 内蔵コード変換表
  private static readonly char[] alphatable = new char[] { };
  
  // エンコーディングメソッド
  internal static string AlphaEncode(string str) { }
 
  // デコーディングメソッド
  internal static string AlphaDecode(string str) { }
}
内蔵コード変換表
6ビットエンコーディングの場合バイナリの111111が最大値になり、アルファコード表は64個文字を用意する必要があります。5ビットエンコーディングにする場合最大11111=32文字となり、プログラムに内蔵する変換表は以下の定義です。
private static readonly char[] alphatable = new char[] {
  'a', 'b', 'c', 'd', 'e', 'f', 'g', 
  'h', 'i', 'j', 'k', 'l', 'm', 'n',
  'o', 'p', 'q', 'r', 's', 't',
  'u', 'v', 'w', 'x', 'y', 'z',
  '0', '1', '2', '3', '4', '5',
};
配列に32個のCharを格納しています。この変換表の場合データは以下の表のように変換されます。
表:8ビットと5ビットのエンコーディング比較
エンコーディングメソッド
internal static string AlphaEncode(string str)
{
  // システムEncodingを利用して文字列をBytesに変換する
  byte[] b = Encoding.Default.GetBytes(str);
  // 8ビットデータを5ビットに変換する際のデータの長さ
  int len = b.Length * 8;
  byte[] buf = new byte[len / 5 + 1];
  // ビット変換
  for (int i = 0; i < len; i++)
  {
    buf[i / 5] |= (byte)(((b[i / 8] >> (7 - (i % 8))) & 1) << (4 - (i % 5)));
  }
  // 新しい文字列のバッファを作成する
  char[] cbuf = new char[buf.Length];
  for (int i = 0; i < buf.Length; i++)
  {
    // 5ビットデータを変換表の文字に変換する
    cbuf[i] = alphatable[buf[i]];
  }
  // 変換結果の文字列を作成し戻す
  return new string(cbuf);
}
デコーディングメソッド
internal static string AlphaDecode(string str)
{
  byte[] bytes = new byte[str.Length];
  for (int i = 0; i < bytes.Length; i++)
  {
    // データが変換表にある位置Indexを探す
    bytes[i] = (byte)Array.IndexOf(alphatable, str[i]);
  }
 
  // 8ビットに変換する際の長さ
  int len = bytes.Length * 5;
  byte[] buf = new byte[len / 8];
  // 5ビット⇒8ビットのバイナリ変換
  for (int i = 0; i < len; i++)
  {
    if (i / 8 >= buf.Length) break;
    buf[i / 8] |= (byte)(((bytes[i / 5] >> (4 - (i % 5))) & 1) << (7 - (i % 8)));
  }
  // 文字列を作成し戻す
  return Encoding.Default.GetString(buf);
}
テストコード
string pwd = "abc"; string encoded = CodecToolkit.AlphaEncode(pwd); string decoded = CodecToolkit.AlphaDecode(encoded); Console.WriteLine(pwd + " -> " + encoded + " -> " + decoded);
出力内容を確認します。
abc -> mfrgg -> abc
内蔵コード変換表を変更してより読みにくくする
private static readonly char[] alphatable = new char[] {
  '!', '@', '#', '$', '%', '^', '&', 
  '*', '(', ')', '_', '+', '[', ']',
  '{', '}', '\\', '|', ';', ':',
  '\'', '"', ',', '.', '/', '?',
  '<', '>', '`', '~', '0', 'O',
};
出力内容は以下の通りです。
abc -> [^|&& -> abc
日本語テスト
日本語テスト -> ;}~)[0`[~_@,+!</\]:\ -> 日本語テスト
符号だけを使っていますので人間にはかなり読みにくくなります。さらにASCII表の32以下の制御符などのCharにすると画面への表示すらもできなくなり、バイナリデータとして保存する際に最適です。
Web掲載、メール添付する際に符号のみを利用する場合暗号化後の文字列が正しく表示できない場合がありますので順番を変えてアルファベットの文字だけを利用するといいでしょう。
以上。
