Node.js で MySQL の AES_ENCRYPT 互換の暗号化

Node.js で MySQL の AES_ENCRYPT 互換の暗号化

September 28, 2020
Misc
MySQL, Node.js, Encryption

MySQL の AES_ENCRYPT 関数 #

https://dev.mysql.com/doc/refman/5.6/ja/encryption-functions.html#function_aes-encrypt

key_str にプレーンテキストを指定する場合 #

HEX 表現で以下の値が得られる。

mysql> SELECT HEX(AES_ENCRYPT('my_value', 'my_secret'));
+-------------------------------------------+
| HEX(AES_ENCRYPT('my_value', 'my_secret')) |
+-------------------------------------------+
| 6A6C5170A935FA5A70B8B1721EDAC3B7          |
+-------------------------------------------+
1 row in set (0.00 sec)

Node.js で同様の結果を得るロジックは以下の通り。

const crypto = require('crypto');

function convertCryptKey(strKey) {
  let newKey = Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
  strKey = Buffer.from(strKey);
  for(let i=0; i < strKey.length; i++) newKey[i%16] ^= strKey[i];
  return newKey;
}

const crypt_key = 'my_secret';
const c = crypto.createCipheriv("aes-128-ecb", convertCryptKey(crypt_key), "");
const b = c.update('my_value', 'utf8', 'hex') + c.final('hex');
const val = Buffer.from(b, 'hex');
console.log(val.toString('hex').toUpperCase());
6A6C5170A935FA5A70B8B1721EDAC3B7

mysqljs/mysql で INSERT して復号確認 #

上記 valmysqljs/mysql 経由で INSERT する。

const mysql = require('mysql');
const conn = require('mysql').createConnection({
  host     : '127.0.0.1',
  user     : 'root',
  password : '',
  database : 'aes_encrypt_test'
});

const sql = `INSERT INTO aes_encrypt_test (col) VALUES (?)`;
conn.query(sql, val, (error, results) => {
  if (error) throw error;
  console.log(results);
  conn.end();
});

MySQL 側で復号できている。

mysql> SELECT AES_DECRYPT(col, 'my_secret') FROM aes_encrypt_test;
+-------------------------------+
| AES_DECRYPT(col, 'my_secret') |
+-------------------------------+
| my_value                      |
+-------------------------------+
1 row in set (0.01 sec)

key_str に 128 ビット値のバイナリを指定する場合 #

mysql> SELECT HEX(AES_ENCRYPT('my_value', UNHEX('F3229A0B371ED2D9441B830D21A390C3')));
+-------------------------------------------------------------------------+
| HEX(AES_ENCRYPT('my_value', UNHEX('F3229A0B371ED2D9441B830D21A390C3'))) |
+-------------------------------------------------------------------------+
| BDECA05C489E3A5F63308C0F34D1DD08                                        |
+-------------------------------------------------------------------------+
1 row in set (0.00 sec)

crypt_key を Buffer を利用して、以下のように変更すれば良い。

// const crypt_key = 'my_secret';
const crypt_key = Buffer.from('F3229A0B371ED2D9441B830D21A390C3', 'hex');
mysql> SELECT AES_DECRYPT(col, UNHEX('F3229A0B371ED2D9441B830D21A390C3')) FROM aes_encrypt_test;
+-------------------------------------------------------------+
| AES_DECRYPT(col, UNHEX('F3229A0B371ED2D9441B830D21A390C3')) |
+-------------------------------------------------------------+
| my_value                                                    |
+-------------------------------------------------------------+
1 row in set (0.00 sec)

Hash 化したパスフレーズを指定する場合 #

mysql> SELECT HEX(AES_ENCRYPT('my_value', SHA2('my_secret',512)));
+-----------------------------------------------------+
| HEX(AES_ENCRYPT('my_value', SHA2('my_secret',512))) |
+-----------------------------------------------------+
| 8777A2DB65E26222698C5E829BE0946A                    |
+-----------------------------------------------------+
1 row in set (0.01 sec)

crypt_key 部分を以下に変更。

const crypt_key = crypto.createHash('sha512').update('my_secret').digest('hex');
mysql> SELECT AES_DECRYPT(col, SHA2('my_secret',512)) FROM aes_encrypt_test;
+-----------------------------------------+
| AES_DECRYPT(col, SHA2('my_secret',512)) |
+-----------------------------------------+
| my_value                                |
+-----------------------------------------+
1 row in set (0.00 sec)

see also #