ActiveX を使用してテキストを暗号化する

久々に ActiveX ネタ。当然のように IE11 でしか動かない例のアレ。

テキストの暗号化と復号を JavaScript だけで行う。

以下の HTML をローカルに保存し、IE11 で開き、セキュリティ設定を許可すると初めて動作する。この時点である意味セキュリティ性が高いかも。w

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <title>Base64</title>
    <style>

* {
  color: #0f0;
  background: #000;
  font: 100%/1 monospace;
}

input,
textarea {
  border: 1px solid #0f0;
  padding: 3px;
}

input[type="text"],
textarea {
  width: 250px;
}

textarea {
  scrollbar-base-color: #000;
  scrollbar-arrow-color: #0f0;
  scrollbar-track-color: #000;
  scrollbar-shadow-color: #0f0;
}

    </style>
    <script>

(function() {
  function crypt(isEncrypt, pass, str) {
    const crypt = new ActiveXObject('System.Security.Cryptography.RijndaelManaged');
    const bytes = new ActiveXObject('System.Text.UTF8Encoding').GetBytes_4(pass);
    const key = new ActiveXObject('System.Security.Cryptography.SHA256Managed');
    const provider = new ActiveXObject('System.Security.Cryptography.MD5CryptoServiceProvider');
    key.ComputeHash_2(bytes);
    crypt.Key = key.Hash;
    key.Clear();
    provider.ComputeHash_2(bytes);
    crypt.IV = provider.Hash;
    provider.Clear();
    const cryptor = isEncrypt ? crypt.CreateEncryptor() : crypt.CreateDecryptor();
    const data = cryptor.TransformFinalBlock(str, 0, getLength(str));
    crypt.Clear();
    return data;
  }
  
  function getLength(bytes) {
    const elem = new ActiveXObject('Msxml2.DOMDocument').createElement('hex');
    elem.dataType = 'bin.hex';
    elem.nodeTypedValue = bytes;
    const len = elem.text.length / 2;
    return len;
  }
  
  function encode(isEncrypt, str) {
    const encoding = new ActiveXObject('System.Text.UTF8Encoding');
    return isEncrypt ? encoding.GetBytes_4(str) : encoding.GetString(str);
  }
  
  function base64(isEncrypt, pass) {
    const elem = new ActiveXObject('Msxml2.DOMDocument').createElement('base64');
    elem.dataType = 'bin.base64';
    
    let base64;
    if(isEncrypt) {
      elem.nodeTypedValue = pass;
      base64 = elem.text;
    }
    else {
      elem.text = pass;
      base64 = elem.nodeTypedValue;
    }
    
    return base64;
  }
  
  function $(id) {
    return document.getElementById(id);
  }
  
  function focusEvent(elem, value) {
    elem.addEventListener('focus', function() {
      if(elem.value === value) {
        elem.value = '';
      }
    });
  }
  
  function blurEvent(elem, value) {
    elem.addEventListener('blur', function() {
      if(elem.value === '') {
        elem.value = value;
      }
    });
  }
  
  function decryptEvent(elem, strElem, cryptElem) {
    elem.addEventListener('click', function() {
      if($('key').value !== '' && cryptElem.value !== '') {
        strElem.value = encode(false, crypt(false, $('key').value, base64(false, cryptElem.value)));
        cryptElem.value = '';
      }
    });
  }
  
  function encryptEvent(elem, strElem, cryptElem) {
    elem.addEventListener('click', function() {
      if($('key').value !== '' && strElem.value !== '') {
        cryptElem.value = base64(true, crypt(true, $('key').value, encode(true, strElem.value)));
        strElem.value = '';
      }
    });
  }
  
  window.onload = function() {
    $('key').value = 'Key';
    $('str1').value = $('str2').value = 'String';
    $('crypt1').value = $('crypt2').value = 'Crypt';
    focusEvent($('key'), 'Key');
    focusEvent($('str1'), 'String');
    focusEvent($('crypt1'), 'Crypt');
    focusEvent($('str2'), 'String');
    focusEvent($('crypt2'), 'Crypt');
    blurEvent($('str1'), 'String');
    blurEvent($('crypt1'), 'Crypt');
    blurEvent($('str2'), 'String');
    blurEvent($('crypt2'), 'Crypt');
    decryptEvent($('decrypt1'), $('str1'), $('crypt1'));
    decryptEvent($('decrypt2'), $('str2'), $('crypt2'));
    encryptEvent($('encrypt1'), $('str1'), $('crypt1'));
    encryptEvent($('encrypt2'), $('str2'), $('crypt2'));
  };
})();

    </script>
  </head>
  <body>
    <p>Key <input type="password" value="" id="key" tabindex="1"></p>
    <p>
      <input type="text" value="" id="str1" tabindex="2">
      <input type="button" value="&lt;-" id="decrypt1" tabindex="5">
      <input type="button" value="-&gt;" id="encrypt1" tabindex="3">
      <input type="text" value="" id="crypt1" tabindex="4">
    </p>
    <p>
      <textarea rows="5" id="str2" tabindex="6"></textarea>
      <input type="button" value="&lt;-" id="decrypt2" tabindex="9">
      <input type="button" value="-&gt;" id="encrypt2" tabindex="7">
      <textarea rows="5" id="crypt2" tabindex="8"></textarea>
    </p>
  </body>
</html>

以下のデモページは IE11 で開いても動作しなかった。

「Key」にパスワードとなる文字列を入力し、「String」のテキストボックスかテキストエリアに暗号化したい文字列を入れる。「->」ボタンを押すと「Crypt」欄に暗号化された文字列が出力されるので、コレをコピーして控えておく。

暗号化文字列を複合する時は、暗号化した際に入力した「Key」を入力し、「Crypt」欄に暗号化文字列を入れ、「<-」ボタンを押す。すると「String」欄に復号した文字列が出力される。「Key」を誤るとスクリプトエラーになる。

任意のキー文字列を使って文字列を暗号化するので、暗号化された文字列だけ分かっても復号できない仕組み。