たびとの旅路 ~電脳砂漠の冒険譚~

フロッピー頼りに歩き、クラウドの地平を見つめる今日まで。見つけたオアシス、迷い込んだ砂の迷宮、全てこの羊皮紙に。

失われし暗号秘儀の再構築 ~C#でOpenSSLの魔法を詠唱する~

どうやら、一筋縄ではいかない砂の迷宮に迷い込んだらしい。この顛末を書き残しておくか。

とあるAPIという名の神殿の扉を開けるには、OpenSSLという古の魔法で暗号化した合言葉が何度も必要だった。 毎回コンソールで呪文を唱える非効率な儀式に私はうんざりしていた。 「この儀式そのものを、C#という名のゴーレムに覚えさせられないか?」…そんな些細な怠け心が、この壮大な冒険の始まりである。

旅の目的は、OpenSSLのencコマンドが生み出す暗号と、C#が生み出す暗号が、寸分違わぬ同一のものであることを証明すること。 特にOpenSSLのバージョンによって作法が異なる「SHA256+PBKDF2」と古の「MD5」という二つの魔法体系を完全に再現する。 情報の砂漠を彷徨い古文書の断片を繋ぎ合わせ、ついに辿り着いたその秘儀のすべてをこの羊皮紙に刻み込む。

この羊皮紙のあらまし

この羊皮紙が導く者

  • OpenSSLのencコマンドという、古の暗号魔法の真髄に触れたい探求者
  • その難解な魔法を、C#という現代の言葉で再現したいと願う魔法使い
  • レガシーシステムとの暗号化通信を実装する必要がある開発者
  • 情報の砂漠で、暗号化という迷宮の出口を探している全ての冒険者

砂漠の道標

  • OpenSSL - 暗号化通信やデジタル証明書を扱うためのオープンソースライブラリ。セキュアな通信の基盤となる。
  • 暗号化 - データを第三者に読めない形に変換する技術。復号化により元のデータに戻せる。
  • AES-256-CBC - 高度暗号化標準(AES)の一種。256ビットの鍵長を持ち、CBCモードで動作する強固な暗号方式。
  • PBKDF2(Password-Based Key Derivation Function 2) - パスワードから暗号鍵を生成する関数。繰り返し処理により解読を困難にする。
  • MD5 - メッセージダイジェスト5。ハッシュ関数の一種だが、現在は脆弱性が知られており非推奨。
  • Base64 - バイナリデータを印字可能なASCII文字列に変換するエンコード方式。メールやテキストでの転送に使われる。

第一の儀式:古文書の解読(openssl encコマンド)

まずは我々が再現すべき古の魔法、openssl encの作法を理解する。 Ubuntu(Linuxディストリビューション)という名の賢者に尋ねると、OpenSSLのバージョンは3.0.13。 そのヘルプという名の巻物を広げると、無数のオプションが記されている。

その中でも今回の儀式で極めて重要な役割を果たす呪文の詠唱法(オプション)は以下の通りだ。 これらを正確に組み合わせることで初めて魔法は正しく発動する。

enc オプション 詠唱法(説明)
-e 暗号化の章を唱える
-d 複合化の章を唱える
-aes-256-cbc AES-256-CBCという術式を用いる
-md val sha256またはmd5で魂を練り上げる
-base64 結果をBase64という異国の言葉に変換する
-A 異国の言葉を一行で記述する
-pass val パスフレーズという合言葉を用いる
-pbkdf2 PBKDF2という秘儀で合言葉を強化する
-iter +int 秘儀のストレッチング回数を指定する(標準10,000回)

新世界の魔法:SHA256 + PBKDF2

現代の標準的な詠唱法。 これで「Ubuntu:SHA256 + PBKDF2」という言葉を暗号化し、それを再び複合化して、儀式が可逆であることを確認する。

# 暗号化の詠唱
$ echo -n 'Ubuntu:SHA256 + PBKDF2' | openssl enc -e -aes-256-cbc -md sha256 -base64 -A -pass pass:password -pbkdf2
U2FsdGVkX180uM6YCr78gph0+q9msr+1uQ+ypYaJ3OlN8Dvx4dm/LPKfEK/N0ukM
# 複合化の詠唱
$ echo -n 'U2FsdGVkX1...ukM' | openssl enc -d -aes-256-cbc -md sha256 -base64 -A -pass pass:password -pbkdf2
Ubuntu:SHA256 + PBKDF2

古の魔法:MD5

OpenSSL 1.1.0未満で標準だった、今や非推奨の詠唱法。 賢者からは警告(WARNING)を受けるが、古のゴーレムと対話するためには、この魔法も習得せねばならない。

# 暗号化の詠唱
$ echo -n 'Ubuntu:MD5' | openssl enc -e -aes-256-cbc -md md5 -base64 -A -pass pass:password
*** WARNING : deprecated key derivation used.
...
U2FsdGVkX1+q3wHjghZvztfesMppgfFP917KweDAtYA=
# 複合化の詠唱
$ echo -n 'U2FsdGVkX1...AtYA=' | openssl enc -d -aes-256-cbc -md md5 -base64 -A -pass pass:password
*** WARNING : deprecated key derivation used.
...
Ubuntu:MD5

第二の儀式:C#による魔法の再構築

いよいよこの二つの魔法体系を.NET 8.0(C#のランタイム環境)という現代の言葉で再現する。 この呪文(コード)の核心は、パスフレーズとSalt(塩:暗号強度を高めるランダム値)から鍵(Key)と初期化ベクトル(IV)を導き出すDeriveKeyAndIVの儀式にある。

(byte[], byte[]) DeriveKeyAndIV(string passphrase, byte[] salt)
{
    byte[] key, iv;
    var pass = Encoding.UTF8.GetBytes(passphrase);
    if (isPbkdf2)
    {
        // 新世界の魔法:SHA256 + PBKDF2
        using var derivedBytes = new Rfc2898DeriveBytes(pass, salt, 10000, HashAlgorithmName.SHA256);
        key = derivedBytes.GetBytes(32);
        iv = derivedBytes.GetBytes(16);
    }
    else
    {
        // 古の魔法:MD5
        // この難解な詠唱法は、古文書の断片を繋ぎ合わせて再現した秘儀である
        var hash1 = MD5.HashData([.. pass, .. salt]);
        var hash2 = MD5.HashData([.. hash1, .. pass, .. salt]);
        key = (byte[])[.. hash1, .. hash2];
        iv = MD5.HashData([.. hash2, .. pass, .. salt]);
    }
    return (key, iv);
}

MD5の作法は公式の古文書には記されておらずStack Overflow(プログラマーのQ&Aサイト)という名の賢者の集会所で幾多の議論の末にようやく再現された、まさに失われし秘儀だ。

この核心部分を組み込んだ完全な呪文を唱え実行した結果がこれだ。

Encrypt-1: U2FsdGVkX1/IMEcoUYTS3hIIoOLOTUA4JPX8ynfjdxYl/7dKvmPWfADXcux6gWpt
Decrypt-1: C#: SHA256 + PBKDF2

Encrypt-2: U2FsdGVkX18Hcilj4MDr3lbWmhiDq8yYDNA/clavnPM=
Decrypt-2: C#: MD5

Decrypt-3: U2FsdGVkX180uM6YCr78gph0+q9msr+1uQ+ypYaJ3OlN8Dvx4dm/LPKfEK/N0ukM
Decrypt-3: Ubuntu:SHA256 + PBKDF2

Decrypt-4: U2FsdGVkX1+q3wHjghZvztfesMppgfFP917KweDAtYA=
Decrypt-4: Ubuntu:MD5

C#で生み出した暗号文がOpenSSLの古の魔法で正しく複合化できることも確認できた。 我々はついに二つの世界の言葉を繋ぐことに成功したのだ。

羊皮紙を巻く前に

今回のOpenSSL暗号秘儀の再現は情報の砂漠を彷徨う困難な旅だった。 GitHubにあるOpenSSLのソースコードという名の聖典はあまりに難解で途中で投げ出してしまったほどだ。 結局、我々を救ってくれたのはStack Overflowという賢者の集会所に残された先人たちの議論の軌跡だった。

OpenSSL暗号化の核心価値

  1. 二つの魔法体系の完全再現 - SHA256+PBKDF2とMD5、両方の暗号化方式をC#で寸分違わず実装
  2. 失われし秘儀の発掘 - 公式文書に記されていないMD5の鍵導出アルゴリズムを先人の知恵から復元
  3. 実用的な自動化への道 - この呪文をクラス化すれば、繰り返しの手動暗号化作業から解放される
  4. レガシーシステムとの架け橋 - 古のMD5方式にも対応することで、既存システムとの互換性を確保

注意すべき点

  • MD5方式は非推奨 - セキュリティ上の脆弱性があるため、新規開発では必ずSHA256+PBKDF2を使用すること
  • Saltはランダム生成 - 同じパスフレーズでも毎回異なる暗号文が生成されるよう、Saltは適切にランダム化すること
  • 例外処理の実装 - 本記事のコードは核心部分のみ。実用化には適切なエラーハンドリングが必須

まとめ

この羊皮紙に記したC#の呪文を元にクラス化と例外処理という鎧を着せれば、冒頭で述べた非効率な儀式を自動化する頼れるゴーレムを創り出せるだろう。 この記録が同じように暗号の迷宮で迷う未来の冒険者の助けとなることを願う。

焚き火の火も小さくなってきた。夜が更ける前に筆を置くとしよう。

砂漠で見つけた魔法のランプ

ラクダの独り言

ご主人が「あんごうか」とかいう誰にも読めない言葉を作るのに夢中になっている。 そんなに隠したいことがあるなら最初から言わなきゃいいのに、と思うのは俺だけか? どうせ俺の晩飯のことなんて誰も読めやしねえのにな。 それにしても、新しい魔法だの古い魔法だのと、やたら難しい呪文が増えたもんだ。 おっとまた腹が鳴っちまった。