どうやら、一筋縄ではいかない砂の迷宮に迷い込んだらしい。この顛末を書き残しておくか。
とあるAPIという名の神殿の扉を開けるには、OpenSSLという古の魔法で暗号化した合言葉が何度も必要だった。毎回コンソールで呪文を唱える非効率な儀式に、私はうんざりしていた。「この儀式そのものを、C#という名のゴーレムに覚えさせられないか?」…そんな些細な怠け心が、この壮大な冒険の始まりである。
旅の目的は、OpenSSLのencコマンドが生み出す暗号と、C#が生み出す暗号が、寸分違わぬ同一のものであることを証明すること。特に、OpenSSLのバージョンによって作法が異なる「SHA256+PBKDF2」と、古の「MD5」という、二つの魔法体系を完全に再現する。情報の砂漠を彷徨い、古文書の断片を繋ぎ合わせ、ついに辿り着いたその秘儀のすべてを、この羊皮紙に刻み込む。
この羊皮紙のあらまし
この羊皮紙が導く者
- OpenSSLの
encコマンドという、古の暗号魔法の真髄に触れたい探求者 - その難解な魔法を、C#という現代の言葉で再現したいと願う魔法使い
- 情報の砂漠で、暗号化という迷宮の出口を探している全ての冒険者
第一の儀式:古文書の解読(openssl encコマンド)
まずは、我々が再現すべき古の魔法、openssl encの作法を理解する。
Ubuntuという名の賢者に尋ねると、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という現代の言葉で再現する。
この呪文(コード)の核心は、パスフレーズと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という名の賢者の集会所で、幾多の議論の末にようやく再現された、まさに失われし秘儀だ。
この核心部分を組み込んだ完全な呪文を唱え、実行した結果がこれだ。
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という賢者の集会所に残された、先人たちの議論の軌跡だった。 この羊皮紙に記したC#の呪文を元に、クラス化と例外処理という鎧を着せれば、冒頭で述べた非効率な儀式を自動化する、頼れるゴーレムを創り出せるだろう。
この記録が、同じように暗号の迷宮で迷う、未来の冒険者の助けとなることを願う。
焚き火の火も小さくなってきた。夜が更ける前に、筆を置くとしよう。
砂漠で見つけた魔法のランプ
ラクダの独り言
ご主人が「あんごうか」とかいう、誰にも読めない言葉を作るのに夢中になっている。そんなに隠したいことがあるなら、最初から言わなきゃいいのに、と思うのは俺だけか?どうせ俺の晩飯のことなんて、誰も読めやしねえのにな。おっと、また腹が鳴っちまった。