はじめに
PHPでの暗号化/複合化は、open_ssl で行っていたけれども、フォームの送信後に暗号化するのではなくて、送信前に暗号化して、それをPHPで後々複合化しようと考えた。
やってみた
自分で暗号化スクリプトを組めればかっこいいのだけれども、できないので、検索して探したところ、以下の二つでやりたいことを実現できた。
crypto-jsを使う
まずは、crypto-js。やり方がわかったのは、AES-128-CBC での方法。
ダウンロードしたcrypto-jsから、aes.jsを取り出す。して、以下のコードで暗号化する。
<script src="aes.js"></script>
<script type="text/javascript">
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
var secret = "この文字列を暗号化します。";
var encrypted = CryptoJS.AES.encrypt(secret, key, {iv:iv});
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
console.log(encrypted);
</script>
続いて、PHP側では、
<?php
$key = hex2bin("0123456789abcdef0123456789abcdef");
$iv = hex2bin("abcdef9876543210abcdef9876543210");
$encrypted = "ジャバスクリプトで暗号化されたテキスト";
$decrypted = openssl_decrypt($encrypted, 'AES-128-CBC', $key, 0, $iv);
$decrypted = trim($decrypted);
echo $decrypted;
?>
で複合化できる。
だけれども、キーと初期ベクトルがJS側で簡単に確認できてしまう。それがわかると複合化もできてしまう。
jsencryptを使う
なので、秘密鍵と公開鍵を使った方法であれば、ジャバスクリプトで暗号化してもソースから複合化はできないので、そちらの方法を考えた。
いろいろと試したのだけれども、jsencryptを使用した方法でうまくいったので、書き留めておく。
opensslを使用して秘密鍵と公開鍵を作成する。(僕はmacなので)ターミナルで、以下を打ち込む
//秘密鍵作成
openssl genrsa 2048 -aes256 -out private_key.pem
//公開鍵作成
openssl rsa -in private_key.pem -pubout -out public_key.pem
続いて、ジャバスクリプト
<script src="jsencrypt.min.js"></script>
<script language="javascript" type="text/javascript">
const obj = JSON.parse(json);
var key = "public_key.pemの中身";
var en_txt = "暗号化したい文章";
var enc = new JSEncrypt();
enc.setPublicKey(key);
console.log(enc.encrypt(en_txt));
</script>
で、暗号化ができる。できるのだけれども注意点がある。public_key.pemのテキスト。
public_key.pemは、
-----BEGIN PUBLIC KEY-----
〜 中略 〜
-----END PUBLIC KEY-----
の形で保存されていて、改行を含めて読み込まないとうまくいかない。が、そのまま変数に入れてもエラーになり、改行を \nに置き換えてもうまくいかない。
調べたところ、ジャバスクリプトにはヒアドキュメントがないので、こういった場合、特殊な方法でテキストを取得する必要があるらしい。たとえば、以下のように、別の箇所にスクリプトタグを使用して、記載しておく。
<script type="text/plain" id="text">
-----BEGIN PUBLIC KEY-----
〜 中略 〜
-----END PUBLIC KEY-----
</script>
それを、var key = document.getElementById("text").textContent.trim(); で取り出して使用する。この場合は暗号化ができる。
XMLHttpRequestでもできるだろうけども、何となくいやだったのと、公開鍵なので、見えてても問題ない。でも、ちょっとかっこ悪いので、APIなどでJSONを取得してできないか試したところ、JSONで一行ずつ値を持たせて、それらを改行を加えつつつなげることでも暗号化ができた。
例
var json = '{"code1":"-----BEGIN PUBLIC KEY-----","code2":"hogehoge"~中略-,"code9":"-----END PUBLIC KEY-----"}';
var obj = JSON.parse(json);
var key = obj.code1+"\n"+obj.code2 〜 中略 〜 +"\n"+obj.code9;
そして、PHP側。PHP側は今まで通りで問題ない。
<?php
$msg ="ジャバスクリプトで暗号化された文章";
$keyPath = 'priveteKey.pem';
$key = file_get_contents($keyPath);
$decodeBody = base64_decode($msg);
openssl_private_decrypt($decodeBody, $decrypted, $key);
echo $decrypted;
?>