古典暗号ツールセット

ツール


🔐 古典暗号の一覧と説明

暗号名説明
アフィン暗号<br>(Affine Cipher)アルファベットを数値に変換して、線形変換 E(x) = (a×x + b) mod 26 を使って暗号化する方式。a と 26 が互いに素である必要があるよ。復号には a の逆元が必要!
シーザー暗号<br>(Caesar Cipher)アルファベットを一定の数だけずらすシンプルな暗号。たとえばシフト3なら A→D、B→E、…Z→C。ジュリアス・シーザーが使っていたことで有名!
ヴィジュネル暗号<br>(Vigenère Cipher)キーワードを使って、文字ごとに異なるシフト量で暗号化する方式。シーザー暗号の進化版で、繰り返しのパターンを隠すのが得意!
アトバッシュ暗号<br>(Atbash Cipher)アルファベットを逆順に置き換える暗号。A↔Z、B↔Y、C↔X…という風に、鏡に映したような変換をするよ。とってもシンプル!
プレイフェア暗号<br>(Playfair Cipher)5×5のマトリクスを使って、2文字ずつペアで暗号化する方式。同じ行・列・長方形の位置関係で文字を入れ替える。J は I と同じ扱いになるよ。
レールフェンス暗号<br>(Rail Fence Cipher)文字をジグザグに並べて、上から順に読んで暗号化する方式。レールの数を変えることでパターンが変わるよ。見た目が波みたいで楽しい!
スキュタレー暗号<br>(Scytale Cipher)古代スパルタで使われた、円筒に巻きつけて書くような暗号。文字列を一定の列数で並べて、縦に読んで暗号化するよ。シンプルだけど効果的!

古典暗号ツールセット

?? 古典暗号ツールセット

テキストを入力して、好きな暗号方式でエンコード/デコードしてみよう!



?? アフィン暗号

線形変換 E(x) = (a×x + b) mod 26 を使う暗号



?? シーザー暗号

アルファベットを一定数ずらす単純な暗号


?? ヴィジュネル暗号

キーワードに基づいて文字ごとに異なるシフトをかける多段階暗号


?? アトバッシュ暗号

アルファベットを逆順に置き換える鏡文字暗号

?? プレイフェア暗号

5×5のマトリクスを使って2文字ずつ変換するペア暗号


?? レールフェンス暗号

文字をジグザグに並べて再構成する波型暗号


?? スキュタレー暗号

文字列を一定の列数で並べて縦読みする古代スパルタの暗号


?? 逆順ツール

文字列を逆さに並べ替える単純変換


結果:

プログラム

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>古典暗号ツールセット</title>
  <style>
    body { font-family: sans-serif; line-height: 1.6; padding: 20px; }
    fieldset { margin-bottom: 20px; }
    #result {
      font-weight: bold;
      background: #eef;
      padding: 5px;
      display: inline-block;
      margin-top: 5px;
      white-space: pre-wrap;
    }
  </style>
</head>
<body>
  <h1>?? 古典暗号ツールセット</h1>
  <p>テキストを入力して、好きな暗号方式でエンコード/デコードしてみよう!</p>

  <label>テキスト: <input type="text" id="text" size="60"></label><br><br>

  <!-- アフィン暗号 -->
  <fieldset>
    <legend>?? アフィン暗号</legend>
    <p>線形変換 E(x) = (a×x + b) mod 26 を使う暗号</p>
    <label>a:
      <select id="a">
        <option value="1">1</option>
        <option value="3">3</option>
        <option value="5" selected>5</option>
        <option value="7">7</option>
        <option value="9">9</option>
        <option value="11">11</option>
        <option value="15">15</option>
        <option value="17">17</option>
        <option value="19">19</option>
        <option value="21">21</option>
        <option value="23">23</option>
        <option value="25">25</option>
      </select>
    </label><br>
    <label>b:
      <input type="range" id="b" min="0" max="25" value="8" oninput="updateBValue(this.value)">
      <span id="bValue">8</span>
    </label><br>
    <button onclick="encryptAffine()">エンコード</button>
    <button onclick="decryptAffine()">デコード</button>
  </fieldset>

  <!-- シーザー暗号 -->
  <fieldset>
    <legend>?? シーザー暗号</legend>
    <p>アルファベットを一定数ずらす単純な暗号</p>
    <label>シフト量:
      <input type="number" id="shift" value="3" min="1" max="25">
    </label><br>
    <button onclick="encryptCaesar()">エンコード</button>
    <button onclick="decryptCaesar()">デコード</button>
  </fieldset>

  <!-- ヴィジュネル暗号 -->
  <fieldset>
    <legend>?? ヴィジュネル暗号</legend>
    <p>キーワードに基づいて文字ごとに異なるシフトをかける多段階暗号</p>
    <label>キーワード: <input type="text" id="vigenereKey" value="KEY"></label><br>
    <button onclick="encryptVigenere()">エンコード</button>
    <button onclick="decryptVigenere()">デコード</button>
  </fieldset>

  <!-- アトバッシュ暗号 -->
  <fieldset>
    <legend>?? アトバッシュ暗号</legend>
    <p>アルファベットを逆順に置き換える鏡文字暗号</p>
    <button onclick="atbash()">変換</button>
  </fieldset>

  <!-- プレイフェア暗号 -->
  <fieldset>
    <legend>?? プレイフェア暗号</legend>
    <p>5×5のマトリクスを使って2文字ずつ変換するペア暗号</p>
    <label>キーワード: <input type="text" id="playfairKey" value="MONARCHY"></label><br>
    <button onclick="encryptPlayfair()">エンコード</button>
    <button onclick="decryptPlayfair()">デコード</button>
  </fieldset>

  <!-- レールフェンス暗号 -->
  <fieldset>
    <legend>?? レールフェンス暗号</legend>
    <p>文字をジグザグに並べて再構成する波型暗号</p>
    <label>レール数: <input type="number" id="rails" value="3" min="2" max="10"></label><br>
    <button onclick="encryptRailFence()">エンコード</button>
    <button onclick="decryptRailFence()">デコード</button>
  </fieldset>

  <!-- スキュタレー暗号 -->
  <fieldset>
    <legend>?? スキュタレー暗号</legend>
    <p>文字列を一定の列数で並べて縦読みする古代スパルタの暗号</p>
    <label>列数(幅): <input type="number" id="scytaleCols" value="4" min="2" max="20"></label><br>
    <button onclick="encryptScytale()">エンコード</button>
    <button onclick="decryptScytale()">デコード</button>
  </fieldset>

  <!-- 逆順 -->
  <fieldset>
    <legend>?? 逆順ツール</legend>
    <p>文字列を逆さに並べ替える単純変換</p>
    <button onclick="reverseText()">逆順にする</button>
  </fieldset>

  <br>
  <button onclick="copyResult()">コピー</button>
  <p>結果: <span id="result"></span></p>

  <script>
    const m = 26;

    function updateBValue(val) {
      document.getElementById("bValue").textContent = val;
    }

    function modInverse(a, m) {
      a = ((a % m) + m) % m;
      for (let x = 1; x < m; x++) {
        if ((a * x) % m === 1) return x;
      }
      throw new Error("逆元が存在しません");
    }

    function encryptAffine() {
      const text = document.getElementById("text").value.toLowerCase();
      const a = parseInt(document.getElementById("a").value);
      const b = parseInt(document.getElementById("b").value);
      let result = "";
      for (let c of text) {
        if (c >= 'a' && c <= 'z') {
          const x = c.charCodeAt(0) - 97;
          const y = (a * x + b) % m;
          result += String.fromCharCode(y + 97);
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function decryptAffine() {
      const text = document.getElementById("text").value.toLowerCase();
      const a = parseInt(document.getElementById("a").value);
      const b = parseInt(document.getElementById("b").value);
      const a_inv = modInverse(a, m);
      let result = "";
      for (let c of text) {
        if (c >= 'a' && c <= 'z') {
          const y = c.charCodeAt(0) - 97;
          const x = (a_inv * (y - b + m)) % m;
          result += String.fromCharCode(x + 97);
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function encryptCaesar() {
      const text = document.getElementById("text").value.toLowerCase();
      const shift = parseInt(document.getElementById("shift").value);
      let result = "";
      for (let c of text) {
        if (c >= 'a' && c <= 'z') {
          const x = (c.charCodeAt(0) - 97 + shift) % m;
          result += String.fromCharCode(x + 97);
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function decryptCaesar() {
      const text = document.getElementById("text").value.toLowerCase();
      const shift = parseInt(document.getElementById("shift").value);
      let result = "";
      for (let c of text) {
        if (c >= 'a' && c <= 'z') {
          const x = (c.charCodeAt(0) - 97 - shift + m) % m;
          result += String.fromCharCode(x + 97);
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function encryptVigenere() {
      const text = document.getElementById("text").value.toLowerCase();
      const key = document.getElementById("vigenereKey").value.toLowerCase();
      let result = "", j = 0;
      for (let i = 0; i < text.length; i++) {
        const c = text[i];
        if (c >= 'a' && c <= 'z') {
          const shift = key.charCodeAt(j % key.length) - 97;
          const x = (c.charCodeAt(0) - 97 + shift) % 26;
          result += String.fromCharCode(x + 97);
          j++;
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function decryptVigenere() {
      const text = document.getElementById("text").value.toLowerCase();
      const key = document.getElementById("vigenereKey").value.toLowerCase();
      let result = "", j = 0;
      for (let i = 0; i < text.length; i++) {
        const c = text[i];
        if (c >= 'a' && c <= 'z') {
          const shift = key.charCodeAt(j % key.length) - 97;
          const x = (c.charCodeAt(0) - 97 - shift + 26) % 26;
          result += String.fromCharCode(x + 97);
          j++;
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function atbash() {
      const text = document.getElementById("text").value.toLowerCase();
      let result = "";
      for (let c of text) {
        if (c >= 'a' && c <= 'z') {
          const x = 25 - (c.charCodeAt(0) - 97);
          result += String.fromCharCode(x + 97);
        } else {
          result += c;
        }
      }
      document.getElementById("result").textContent = result;
    }

    function reverseText() {
      const text = document.getElementById("text").value;
      document.getElementById("result").textContent = text.split("").reverse().join("");
    }

    function copyResult() {
      const result = document.getElementById("result").textContent;
      navigator.clipboard.writeText(result).then(() => {
      });
    }

    function encryptRailFence() {
      const text = document.getElementById("text").value.replace(/[^a-zA-Z]/g, "");
      const numRails = parseInt(document.getElementById("rails").value);
      if (numRails < 2) return alert("レール数は2以上にしてね!");
      let rails = Array.from({ length: numRails }, () => []);
      let rail = 0, direction = 1;
      for (let char of text) {
        rails[rail].push(char);
        rail += direction;
        if (rail === 0 || rail === numRails - 1) direction *= -1;
      }
      const result = rails.flat().join("");
      document.getElementById("result").textContent = result;
    }

    function decryptRailFence() {
      const text = document.getElementById("text").value.replace(/[^a-zA-Z]/g, "");
      const numRails = parseInt(document.getElementById("rails").value);
      if (numRails < 2) return alert("レール数は2以上にしてね!");
      const len = text.length;
      let pattern = Array(len).fill(0);
      let rail = 0, direction = 1;
      for (let i = 0; i < len; i++) {
        pattern[i] = rail;
        rail += direction;
        if (rail === 0 || rail === numRails - 1) direction *= -1;
      }
      let railCounts = Array(numRails).fill(0);
      for (let r of pattern) railCounts[r]++;
      let rails = [], pos = 0;
      for (let count of railCounts) {
        rails.push(text.slice(pos, pos + count).split(""));
        pos += count;
      }
      let result = "";
      for (let r of pattern) {
        result += rails[r].shift();
      }
      document.getElementById("result").textContent = result;
    }

    function encryptScytale() {
      const text = document.getElementById("text").value.replace(/[^a-zA-Z]/g, "");
      const cols = parseInt(document.getElementById("scytaleCols").value);
      const rows = Math.ceil(text.length / cols);
      let grid = Array.from({ length: rows }, () => Array(cols).fill(""));
      for (let i = 0; i < text.length; i++) {
        const row = Math.floor(i / cols);
        const col = i % cols;
        grid[row][col] = text[i];
      }
      let result = "";
      for (let col = 0; col < cols; col++) {
        for (let row = 0; row < rows; row++) {
          if (grid[row][col]) result += grid[row][col];
        }
      }
      document.getElementById("result").textContent = result;
    }

    function decryptScytale() {
      const text = document.getElementById("text").value.replace(/[^a-zA-Z]/g, "");
      const cols = parseInt(document.getElementById("scytaleCols").value);
      const rows = Math.ceil(text.length / cols);
      const shortCols = cols * rows - text.length;
      let grid = Array.from({ length: rows }, () => []);
      let index = 0;
      for (let col = 0; col < cols; col++) {
        for (let row = 0; row < rows; row++) {
          if (col >= cols - shortCols && row === rows - 1) continue;
          grid[row][col] = text[index++];
        }
      }
      let result = "";
      for (let row = 0; row < rows; row++) {
        for (let col = 0; col < cols; col++) {
          if (grid[row][col]) result += grid[row][col];
        }
      }
      document.getElementById("result").textContent = result;
    }

    function generatePlayfairMatrix(key) {
      key = key.toLowerCase().replace(/[^a-z]/g, "").replace(/j/g, "i");
      let seen = new Set();
      let matrix = [];
      for (let c of key + "abcdefghijklmnopqrstuvwxyz") {
        if (c === 'j') c = 'i';
        if (!seen.has(c)) {
          seen.add(c);
          matrix.push(c);
        }
      }
      return matrix.slice(0, 25);
    }

    function findPosition(matrix, char) {
      if (char === 'j') char = 'i';
      const index = matrix.indexOf(char);
      return [Math.floor(index / 5), index % 5];
    }

    function getChar(matrix, row, col) {
      return matrix[(row + 5) % 5 * 5 + (col + 5) % 5];
    }

    function prepareText(text) {
      text = text.toLowerCase().replace(/[^a-z]/g, "").replace(/j/g, "i");
      let result = "";
      for (let i = 0; i < text.length; i += 2) {
        let a = text[i];
        let b = text[i + 1] || 'x';
        if (a === b) {
          result += a + 'x';
          i--;
        } else {
          result += a + b;
        }
      }
      return result;
    }

    function encryptPlayfair() {
      const key = document.getElementById("playfairKey").value;
      const text = prepareText(document.getElementById("text").value);
      const matrix = generatePlayfairMatrix(key);
      let result = "";
      for (let i = 0; i < text.length; i += 2) {
        const [r1, c1] = findPosition(matrix, text[i]);
        const [r2, c2] = findPosition(matrix, text[i + 1]);
        if (r1 === r2) {
          result += getChar(matrix, r1, c1 + 1) + getChar(matrix, r2, c2 + 1);
        } else if (c1 === c2) {
          result += getChar(matrix, r1 + 1, c1) + getChar(matrix, r2 + 1, c2);
        } else {
          result += getChar(matrix, r1, c2) + getChar(matrix, r2, c1);
        }
      }
      document.getElementById("result").textContent = result;
    }

    function decryptPlayfair() {
      const key = document.getElementById("playfairKey").value;
      const text = prepareText(document.getElementById("text").value);
      const matrix = generatePlayfairMatrix(key);
      let result = "";
      for (let i = 0; i < text.length; i += 2) {
        const [r1, c1] = findPosition(matrix, text[i]);
        const [r2, c2] = findPosition(matrix, text[i + 1]);
        if (r1 === r2) {
          result += getChar(matrix, r1, c1 - 1) + getChar(matrix, r2, c2 - 1);
        } else if (c1 === c2) {
          result += getChar(matrix, r1 - 1, c1) + getChar(matrix, r2 - 1, c2);
        } else {
          result += getChar(matrix, r1, c2) + getChar(matrix, r2, c1);
        }
      }
      document.getElementById("result").textContent = result;
    }
  </script>
</body>
</html>

JavaScript

Posted by eightban