やりたいこと
tripleDESのCBCモードで暗号化したデータを先方のサーバーにリクエストすると、同様の方式でデータがレスポンスされるので復号する。
で、このtripleDESについて全く無知だったのでググってみると、Rubyの記事って案外情報が見つかりにくいんですね。
色々検索してこちらのページにたどり着きました。
http://timolshansky.com/2011/10/23/ruby-triple-des-encryption.html
実際ここで書かれているコードを実行すると、ちゃんと動くんです。が、ここからが長かった。
先方の認証サーバーにリクエストするもうまくいかない
いただいた鍵を使って、その他パラメータを何度確認してもミスはない。ぐぬぬ...
恥を忍んで先方の方が暗号化の際に使っているコード(別の言語)を見せていただくと....おや?暗号化したバイトの先頭に何かくっつけているぞ?
初期化ベクトルというもう一つの鍵
実は、今回の暗号化方式(des-ede3-cbc)では、秘密鍵の他に初期化ベクトルという物を実質鍵のように使って暗号化しているんですね。
これに気づくのに随分と時間がかかった。なんでかっていうと、↑に挙げたページやその他ぐぐったページでは 'pkcs5_keyivgen' というメソッドを使っているんですよ。
要は インスタンス作る -> pkcs5_keyivgenで鍵と初期化ベクトルをセット -> 暗号、復号する という流れなんですが、これだと同じ初期化ベクトルを使っていることになる。だから復号できる。
が、今回の条件は こちらの手元で暗号化 -> 相手サーバーで復号 となるので、相手は秘密鍵だけでなく初期化ベクトルの情報を知らなければいけない んですね。 (先頭に初期化ベクトル入れるってのはtripleDESの仕様なのでしょうか?これはググってもわからんかった)
実装
というわけで、実際にやってみたのがこちら。今回は、先頭8byteが初期化ベクトル。
class TripleDES
class << self
IV_LENGTH = 8
SECRET_KEY = 'your__awesome_Secret_Key'
def get_cipher
cipher = OpenSSL::Cipher.new('des-ede3-cbc')
cipher.key = SECRET_KEY
cipher
end
def encrypt(plain_string)
cipher = get_cipher
cipher.encrypt
# 初期化ベクトルを生成する
iv = OpenSSL::PKCS5.pbkdf2_hmac(SecureRandom.alphanumeric(10), SecureRandom.alphanumeric(10), 2, IV_LENGTH, 'sha1')
cipher.iv = iv
output = cipher.update(plain_string)
output << cipher.final
# 生成した暗号の前に初期化ベクトルを入れる
iv + output
end
def decrypt(encrypted_byte_string)
cipher = get_cipher
cipher.decrypt
# 初期化ベクトルと本文をそれぞれ取り出す
iv = encrypted_byte_string.byteslice(0, IV_LENGTH)
cipher.iv = iv
target_bytes = encrypted_byte_string.byteslice(IV_LENGTH, encrypted_byte_string.chars.count)
output = cipher.update(target_bytes)
output << cipher.final
end
end
end
余談
pkcs5_keyivgenは非推奨メソッドってちゃんと書いてあるのにしばらく気づかなかった。笑
https://docs.ruby-lang.org/ja/latest/method/OpenSSL=3a=3aCipher/i/pkcs5_keyivgen.html
こちらの方が詳しい。そうなんですよ、pkcs5_keyivgenはivを取って来れないんですよね。
https://techmedia-think.hatenablog.com/entry/20110527/1306499951
今後実装される方のヒントになれば幸いです。久々にハマった....