0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScript 暗号化・復号化

Last updated at Posted at 2024-11-22

はじめに

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>

Webアプリケーションサーバ AiR


弊社webページ: AiR

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?