はじめに
暑い日が続いたかと思うと梅雨のように雨が続く。今年の夏は厳しくなりそうだと今から気が滅入っているgrnishiです。こんにちは。
本題
クライアント側で暗号化し、サーバ側で復号する。またはサーバ側で暗号化したテキストをクライアント側で復号する。といった需要はそこそこあると思います。
同じ言語(ライブラリ)でやれば簡単に実装できるのですが、異なる言語間だとイマイチうまくいかない事が多々あります。
というわけでニッチな需要に応えるべく、サンプルを作成しました。
ちなみにニッチの語源は英語で「niche」です。建築業界では窪みなどを指す言葉だそうです。
環境
JavaScript側はこれを使います。
crypto-js https://code.google.com/archive/p/crypto-js/
PHP側はこれを使います。
openssl_encrypt https://www.php.net/manual/ja/function.openssl-encrypt.php
openssl_decrypt https://www.php.net/manual/ja/function.openssl-decrypt.php
暗号メソッドはAES-256-CBC
コード
JavaScript側
/** * 暗号化 */ function encrypt(plain, passphrase){ var salt = CryptoJS.lib.WordArray.random(128); var iv = CryptoJS.lib.WordArray.random(16); var key = CryptoJS.PBKDF2(passphrase, salt, { hasher: CryptoJS.algo.SHA512, iterations: 100, keySize: 8 }); var cipher = CryptoJS.AES.encrypt(plain, key, {iv: iv}); var data = { cipher : CryptoJS.enc.Base64.stringify(cipher.ciphertext), salt : CryptoJS.enc.Hex.stringify(salt), iv : CryptoJS.enc.Hex.stringify(iv) } return data; } /** * 復号 */ function decrypt(cipher, passphrase, salt, iv){ var key = CryptoJS.PBKDF2(passphrase, CryptoJS.enc.Hex.parse(salt), { hasher: CryptoJS.algo.SHA512, iterations: 100 , keySize: 8}); var plain = CryptoJS.AES.decrypt(cipher, key, { iv: CryptoJS.enc.Hex.parse(iv)}).toString(CryptoJS.enc.Utf8); return plain; }
PHP側
<?php /** * 暗号化 */ function encrypt($plain, $passphrase) { $salt = openssl_random_pseudo_bytes(128); $iv = openssl_random_pseudo_bytes(16); $iterations = 100; $key = hash_pbkdf2("sha512", $passphrase, $salt, $iterations, 128); $cipher = openssl_encrypt($plain, "AES-256-CBC", hex2bin($key), OPENSSL_RAW_DATA, $iv); $data = [ "cipher" => base64_encode($cipher), "salt" => bin2hex($salt), "iv" => bin2hex($iv), ]; return $data; } /** * 復号 */ function decrypt($cipher, $passphrase, $salt, $iv){ $iterations = 100; $key = hash_pbkdf2("sha512", $passphrase, hex2bin($salt), $iterations, 128); $plain= openssl_decrypt(base64_decode($cipher), "AES-256-CBC", hex2bin($key), OPENSSL_RAW_DATA, hex2bin($iv)); return $plain; }
結果
JavaScriptで暗号化する
var plain = "あらゆる現実をすべて自分のほうへねじ曲げたのだ"; var passphrase = "passphrase"; var data = encrypt(plain, passphrase); // data.cipher lj57hl3KarnYV2NFZHZVpcrh4Z1rK3aSvolRdrzTimPZ8DzolSDn3bOkgn9rBKNdIx4vs/eV+vASjFQJQs32Wwj8BZUUwhv3+wYm6nombAw= data.salt 38b1576dbcf429da7acd75fdec89d255d4c49e856ce46ca9a9fea4231e4c03b6195ecd5daecd95530ee5a37203fbe7478171f118266ad549563d69e23ec8909dbf2557af2ad6195655e7418023d582b970d6620d3ef770db6c3f0a4427140ceb254b8edc281a44545d3e64a0c2e7256f661df9f32168870bd046612ea8bac6b3 data.iv fa6ed5055e008e19414c6dca4eaae8a3
PHPで復号する
<?php $cipher = "lj57hl3KarnYV2NFZHZVpcrh4Z1rK3aSvolRdrzTimPZ8DzolSDn3bOkgn9rBKNdIx4vs/eV+vASjFQJQs32Wwj8BZUUwhv3+wYm6nombAw="; $salt = "38b1576dbcf429da7acd75fdec89d255d4c49e856ce46ca9a9fea4231e4c03b6195ecd5daecd95530ee5a37203fbe7478171f118266ad549563d69e23ec8909dbf2557af2ad6195655e7418023d582b970d6620d3ef770db6c3f0a4427140ceb254b8edc281a44545d3e64a0c2e7256f661df9f32168870bd046612ea8bac6b3"; $iv = "fa6ed5055e008e19414c6dca4eaae8a3"; $passphrase = "passphrase"; $plain = decrypt($cipher, $passphrase, $salt, $iv); // あらゆる現実をすべて自分のほうへねじ曲げたのだ
PHPで暗号化する
<?php $plain = "あらゆる現実をすべて自分のほうへねじ曲げたのだ"; $passphrase = "passphrase"; $data = encrypt($plain, $passphrase); // $data["cipher"] ZP4b2C3wBwz7eInfcs6r5CHKCZ2C1O/zpxA4w7u+Yf899dOjsO9c07OgYQZ99Su7BjW5gnDahrjuoqLbvH+PB5FCh3361q+ZhR3sX5R55l4= $data["salt"] 8bcdbf1e97e3df12ec23b52f518b7652dac332f466893065b4a18e1ba050057e592e3c53e0d33e97fd79d6cd672376b021264fda786fce8b3554de3b230e75cd5e51cfdc88e217e03a6b488530f4d25f21c229122fde2f5ad43b04001925b0ce18b4f9d8269b37a2b84f09bb4b986b2ad25216492438cd9c195e7f8c2cacd5f1 $data["iv"] 9e7498abc461754140b9700a5c73efa7
JavaScriptで復号する
var cipher = "ZP4b2C3wBwz7eInfcs6r5CHKCZ2C1O/zpxA4w7u+Yf899dOjsO9c07OgYQZ99Su7BjW5gnDahrjuoqLbvH+PB5FCh3361q+ZhR3sX5R55l4="; var salt = "8bcdbf1e97e3df12ec23b52f518b7652dac332f466893065b4a18e1ba050057e592e3c53e0d33e97fd79d6cd672376b021264fda786fce8b3554de3b230e75cd5e51cfdc88e217e03a6b488530f4d25f21c229122fde2f5ad43b04001925b0ce18b4f9d8269b37a2b84f09bb4b986b2ad25216492438cd9c195e7f8c2cacd5f1"; var iv = "9e7498abc461754140b9700a5c73efa7"; var plain = decrypt(cipher, passphrase, salt, iv); // あらゆる現実をすべて自分のほうへねじ曲げたのだ
さいごに
JavaScriptで暗号化→PHPで復号、PHPで暗号化→JavaScriptで復号をやりました。(当然ですが)言語が異なっていても問題なく復号できます。
node.jsやBunなりでサーバサイドもJavaScriptで書けばこんな苦労は無いんですけどね。
大人の事情でそうもいかない人のためになれば幸いです。