はじめに
MH ソフトウェア & サービスが開発・公開している、Webアプリケーションサーバ AiRの便利かな?と思われるコードの紹介です。
Webアプリケーションサーバ AiRはPython、Tornado(Webフレームワーク、Webサーバ)、JavaScript、その他のモジュールで構成されています。
Webアプリケーションサーバ AiRは、httpとhttps(SSL)で稼働させることが可能です。
https(SSL)では通信内容は暗号化されると言われています。
AiRをhttp通信で使用する場合もあります。
そこで、AiRはPythonとJavaScriptで共通の暗号化・復号化を実現し、JavaScriptからのAjaxデータを暗号化して通信する方法を実現しています。
crypto-js.min.jsとbase64.min.jsを使用します。
暗号化キーの長さは16文字です
暗号化キー: 1234567890123456
この文字列を暗号化します: MH ソフトウェア & サービス
暗号化された文字列: beb80c687e1572b9893299376106b08a83029968c4c1b8a28fd8785d76f875e4bb7bcf35289b598316363b6b5306b205
'name'と言う文字列: name
暗号化した'name': 27126826c77d8ebac9b982928795f38a
暗号化した文字列をjson形式でキーと値に入れて暗号化します
暗号化前の文字列: {"27126826c77d8ebac9b982928795f38a":"beb80c687e1572b9893299376106b08a83029968c4c1b8a28fd8785d76f875e4bb7bcf35289b598316363b6b5306b205"}
暗号化: ce7bb1ac32d66c4252dda79d7fc8735129ed1471ab1f9a39fa770ebb6e9bfd28f31c040f42df78327177b8f4b3db2e56a969b132549ee68dce3472ae74f4217decf58c583ad49ab9c40634a2773023902d36a2babef2df4cd3d5a891c073f1be7f9995abaf4da03d94837b6e8e74f178fc21e5bffcffb9eef52fbbca388347601d67d8cf0336fb46781be0c27c1e4a86
暗号化されたJSON文字列を復号化します
[object Object]
Objectなので文字列にします(JSON.stringfy)
{"name":"MH ソフトウェア & サービス"}
下記が今回のサンプルのHTML JavaScriptです。実際にはAiR内に、'fx'というクラスに含まれています。
コードが長くてすいません。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript 暗号化・復号化</title>
<style> input {margin-right: 20px; width: 50%;} </style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<!-- 暗号化・復号化ライブラリを読み込みます -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-base64@3.7.5/base64.min.js"></script>
</head>
<body>
<p>
暗号化キーの長さは16文字です<br>
記号も使えます<br>
<div style="display: flex;">
暗号化キー: <input id="encryptKey" type="text" value="1234567890123456">
</div>
</p>
<p>
<div style="display: flex;">
この文字列を暗号化します: <input id="normalMH" type="text" value="MH ソフトウェア & サービス"><br>
</div>
<div style="display: flex;">
暗号化された文字列:<input id="encryptedMH" type="text" value="">
</div>
</p>
<p>
<div style="display: flex;">
'name'と言う文字列:<input id="nameStr" type="text" value="name"><br>
</div>
<div style="display: flex;">
暗号化した'name':<input id="encryptedName" type="text" value="">
</div>
</p>
<p>
暗号化した文字列をjson形式でキーと値に入れて暗号化します<br>
<div style="display: flex;">
暗号化前の文字列:<input id="normalJson" type="text" value="">
</div>
<div style="display: flex;">
暗号化:<input id="encryptedJson" type="text" value="">
</div>
</p>
<p>
暗号化されたJSON文字列を復号化します<br>
<input id="decryptedJson" type="text" value="">
</p>
<p>
Objectなので文字列にします(JSON.stringfy)<br>
<input id="decryptedJsonStr" type="text" value="">
</p>
<p>
<input type="button" onclick="exec();" id="execute" value="実行">
</p>
<script>
// インデントは気にしないでね・・・
class Decryptc {
/**
* @param {any} encrypted
* @param {Boolean} [log=false]
* @returns {any}
*/
_(encrypted, log=false) {
// If modify this function, modify project://fx/__init__.py#75 def decript
return this.#_(encrypted, log=false);
};
/**
* @param {any} encrypted
* @param {Boolean} [log=false]
* @returns {any}
*/
#_(encrypted, log=false) {
if (!this.#decryptable(encrypted)) {
return encrypted;
};
// NOTE: Create result.
const json = JSON.parse(JSON.stringify(encrypted));
// NOTE: Check encrypted is list or dict?
if (this.#typeof(json) === 'array' || this.#typeof(json) === 'object') {
return this.#object(json);
};
const decrypted = this.#decrypt(json);
const decryptedStr = this.#toString(encrypted, decrypted);
let result = JSON.parse(JSON.stringify(decryptedStr));
// NOTE: Check encrypted is list or dict?
if (this.#typeof(result) === 'array' || this.#typeof(result) === 'object') {
result = this.#object(result);
};
// NOTE: Check decrypted is list or dict?
if (result !== '') {
result = this.#reDecrypt(result, log);
} else {
// IMPORTANCE: decrypted.toString(window['CryptoJS'].enc.Utf8) return ""
// at plane text.
result = encrypted;
};
return result;
};
/**
* @param {String} encrypted
* @returns {any}
*/
#decrypt(encrypted) {
const reb64 = window['CryptoJS'].enc.Hex.parse(encrypted);
const result = window['CryptoJS'].AES.decrypt(
reb64.toString(window['CryptoJS'].enc.Base64),
window['CryptoJS'].enc.Base64.parse(window['Base64'].encode(this.#encryptKey)),
{
keySize: 128 / 8,
iv: window['CryptoJS'].enc.Base64.parse(window['Base64'].encode(this.#encryptKey)),
mode: window['CryptoJS'].mode.CBC,
padding: window['CryptoJS'].pad.Pkcs7
},
);
return result;
};
/**
* @param {Array | Number | Object | String} encrypted
* @returns {Boolean}
*/
#decryptable(encrypted) {
let result = false;
if (
this.#notDecrytableTypes.includes(this.#typeof(encrypted))
|| encrypted === void(0)
|| encrypted === null
) {
result = false;
} else if (this.#decrytableTypes.includes(this.#typeof(encrypted))) {
result = true;
} else if (encrypted.length === 0) {
result = false;
} else {
try {
const _loads = JSON.parse(encrypted);
// NOTE: If loads is number, can't decrypt.
if (this.#typeof(_loads) == 'number') {
result = false;
};
} catch (e) {
result = true;
};
};
return result;
};
/**
* @param {Object} encrypted
* @param {Boolean} [log=false]
* @returns {Object}
*/
#dict(encrypted, log=false) {
/** @type {Object} */
let result = {};
Object.keys(encrypted).forEach((key) => {
result[this.#_(key)] = this.#_(encrypted[key], log);
});
return result;
};
/**
* @returns {String}
*/
get #encryptKey() {
const result = $('#encryptKey').val();
return result;
};
/**
* @param {any[]} encrypted
* @returns {Array}
*/
#list(encrypted) {
let result = JSON.parse(JSON.stringify(encrypted));
encrypted.forEach((data, index) => {
result[index] = this.#_(data);
});
return result;
};
/**
* @param {Array | Object} value
* @returns {Array | Object}
*/
#object(value) {
let result;
if (this.#typeof(value) === 'object') {
result = this.#dict(value);
} else if (this.#typeof(value) === 'array') {;
result = this.#list(value);
};
return result;
};
/**
* @param {String} value
* @param {Boolean} log
* @returns {Array | Object | String}
*/
#reDecrypt(value, log) {
let result;
const defaultValue = value;
try {
const decryptedParse = JSON.parse(value);
if (this.#typeof(decryptedParse) === 'object') {
result = this.#dict(decryptedParse, log);
} else if (this.#typeof(decryptedParse) === 'array') {
result = this.#list(decryptedParse);
} else {
result = decryptedParse;
};
} catch(e) {
result = defaultValue;
};
return result;
};
/**
* @param {String} encrypted
* @param {Object} decrypted
* @returns
*/
#toString(encrypted, decrypted) {
let result;
// IMPORTANCE: Error happen at decrypted is normaly string.
try {
result = decrypted.toString(window['CryptoJS'].enc.Utf8);
} catch (e) {
result = encrypted;
};
return result;
};
/**
* @param {any} object
* @returns {String}
*/
#typeof(object) {
return Object.prototype.toString.call(object).slice(8, -1).toLowerCase();
};
#decrytableTypes = ['object', 'array', 'string'];
#notDecrytableTypes = ['boolean', 'number'];
};
$(() => {
window['decryptc'] = new Decryptc();
});
class Encryptc {
/**
* @param {String} str
* @returns {String}
*/
_(str) {
return this.#_(str);
};
/**
* @param {String} str
* @returns {String}
*/
#_(str) {
let result = '';
if (str !== '') {
const encryptedText = window['CryptoJS'].AES.encrypt(
window['CryptoJS'].enc.Utf8.parse(str),
window['CryptoJS'].enc.Base64.parse(window['Base64'].encode(this.#encryptKey)),
{
keySize: 128 / 8,
iv: window['CryptoJS'].enc.Base64.parse(window['Base64'].encode(this.#encryptKey)),
mode: window['CryptoJS'].mode.CBC,
padding: window['CryptoJS'].pad.Pkcs7
},
);
const b64 = encryptedText.toString();
const e64 = window['CryptoJS'].enc.Base64.parse(b64);
const eHex = e64.toLocaleString(window['CryptoJS'].enc.Hex);
//result = eHex.toUpperCase();
result = eHex;
};
return result;
};
/**
* @returns {String}
*/
get #encryptKey() {
const result = $('#encryptKey').val();
return result;
};
};
$(() => {
window['encryptc'] = new Encryptc();
});
class fx {
constructor() {
window['decrypt'] = this.decrypt;
window['encrypt'] = this.encrypt;
window['exec'] = this.exec;
};
/**
* @param {String} str
* @returns {String}
*/
decrypt(str) {
return window['decryptc']._(str);
};
/**
* @param {String} str
* @returns {String}
*/
encrypt(str) {
return window['encryptc']._(str);
};
exec() {
const normalMH = $('#normalMH').val();
const encryptedMH = window['encrypt'](normalMH);
$('#encryptedMH').val(encryptedMH);
const nameStr = window['encrypt']($('#nameStr').val());
$('#encryptedName').val(nameStr);
const json_ = {[nameStr]: encryptedMH};
const jsonStr = JSON.stringify(json_)
$('#normalJson').val(jsonStr);
const encryptedJson = window['encrypt'](jsonStr);
$('#encryptedJson').val(encryptedJson);
const decryptedJson = window['decrypt'](encryptedJson);
$('#decryptedJson').val(decryptedJson);
$('#decryptedJsonStr').val(JSON.stringify(decryptedJson));
};
};
$(() => {
window['fx'] = new fx();
});
</script>
</body>
</html>