ActiveTK's Note

【JavaScript】テキストやファイルをE2E暗号化して保存する方法


作成日時 2023/09/12 11:37
最終更新 2023/09/12 12:01


  • まず初めに
  • 暗号化
  • 復号

  • まず初めに

    本記事では、JavaScriptでCryptoJSを用いてファイルを暗号化・復号するプログラムを掲載させて頂きます。

    CryptoJSを利用するため、headで以下のスクリプトを読み込んで下さい。

    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>

    暗号化

    // 暗号化するファイル(input)
    var file = _("file").files[0];
    
    // パスワード
    var password = "password";
    
    // ファイルを読み取って暗号化
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      var salt = CryptoJS.lib.WordArray.random(128 / 8);
      var iv = CryptoJS.lib.WordArray.random(128 / 8);
      var enc = CryptoJS.enc.Hex.stringify(salt) + "," + CryptoJS.enc.Hex.stringify(iv) + "," +
      CryptoJS.AES.encrypt(
        CryptoJS.enc.Utf8.parse(reader.result),
        CryptoJS.PBKDF2(
          CryptoJS.enc.Utf8.parse(password),
          salt,
          {
            keySize: 128 / 8,
            iterations: 500
          }
        ),
        {
          iv: iv,
          mode: CryptoJS.mode.CBC,
          padding: CryptoJS.pad.Pkcs7
        }
      );
    
      // inputの選択しているファイルを暗号データに置き換える
      let list = new DataTransfer();
      list.items.add(new File([enc], file.name));
      _("file").files = list.files;
    
      // formをPOSTする処理
      // (略)
    
    }

    復号

    // ファイルダウンロード用の関数
    function DownloadFile(fileurl, callback) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", fileurl);
      xhr.responseType = "arraybuffer";
      xhr.onreadystatechange = function (evt) {
        if (xhr.readyState === 4) {
          if (xhr.status === 200) {
            callback(xhr.response);
          }
        }
      }
      xhr.send();
    }
    
    // ファイルをダウンロード
    var password = "password";
    var fileurl = "https://example.com/example-encrypted.png";
    var FileName = "example-cncrypted.png";
    
    DownloadFile(fileurl, function (result) {
      window.decryptionarray = [];
      window.decryptionarray[0] = String.fromCharCode.apply("", new Int8Array(result.slice(0, 32)));
      window.decryptionarray[1] = String.fromCharCode.apply("", new Int8Array(result.slice(33, 65)));
    
      window.decryptionarray[2] = "";
      var uint8Arr = new Int8Array(result.slice(66));
      for (var i = 0; i < uint8Arr.length; i += 1024)
        window.decryptionarray[2] += String.fromCharCode.apply(null, uint8Arr.slice(i, i + 1024));
    
      var salt = CryptoJS.enc.Hex.parse(window.decryptionarray[0]);
      var iv = CryptoJS.enc.Hex.parse(window.decryptionarray[1]);
    
      try {
        // 復号してダウンロード
        DownloadFileFromBlobURL(
          CryptoJS.AES.decrypt(
            {
              "ciphertext": CryptoJS.enc.Base64.parse(window.decryptionarray[2])
            },
            CryptoJS.PBKDF2(
              CryptoJS.enc.Utf8.parse(password),
              salt,
              {
                keySize: 128 / 8,
                iterations: 500
              }
            ),
            {
              iv: iv,
              mode: CryptoJS.mode.CBC,
              padding: CryptoJS.pad.Pkcs7
            }
          ).toString(CryptoJS.enc.Utf8),
          FileName
        );
      } catch (e) {
        // パスワードが異なっている場合の処理
      }
    });
    
    // blobのURLからダウンロードする関数
    function DownloadFileFromBlobURL(blob, filename) {
      const a = document.createElement("a");
      document.body.appendChild(a);
      a.download = filename;
      a.href = blob;
      a.click();
      a.remove();
    }