sroxck

sroxck

前端RSAとSM4暗号化プロセス

会社の最新の要求は、フロントエンドが秘密鍵を使って暗号を解読することを求めています.... 本当に君子を防ぐためではなく、小人を防ぐためのものですね。ここでは、フロントエンドが RSA、AES、SM4 などの暗号アルゴリズムを使用した暗号化と復号化のプロセス、およびフックのラッピングについて記録します。

秘密鍵と公開鍵#

  1. 公開鍵:
    誰にでも公開でき、データを暗号化するために使用されます。公開鍵で暗号化されたデータは、対応する秘密鍵でのみ復号化できます。
  2. 秘密鍵:
    秘密に保つ必要があり、鍵の所有者のみが保持します。データを復号化するために使用されます。秘密鍵で復号化されたデータは、対応する公開鍵で暗号化された内容のみが復号化可能です。

作業フロー#

  1. 鍵ペアの生成:公開鍵と秘密鍵を含む一対の鍵を生成します。
  2. データの暗号化:送信者は受信者の公開鍵を使用してデータを暗号化します。
  3. データの転送:暗号化されたデータは、安全でないチャネルを通じて安全に送信できます。
  4. データの復号化:受信者は自分の秘密鍵を使用して受信したデータを復号化します。

フロントエンド暗号ライブラリの使用#

RSA 使用 jsencrypt または node-rsa#

import JSEncrypt from "jsencrypt";
// RSA 鍵ペアを生成
export const generateKeyPair = () => {
  const key = new NodeRSA({ b: 512 });
  return {
    publicKey: key.exportKey("public"),
    privateKey: key.exportKey("private"),
  };
};

// RSA 暗号化関数
export const rsaEncrypt = (data, publicKey) => {
  const key = new NodeRSA(publicKey);
  return key.encrypt(data, "base64");
};

// RSA 復号化関数
export const rsaDecrypt = (encryptedData, privateKey) => {
  const key = new NodeRSA(privateKey);
  return key.decrypt(encryptedData, "utf8");
};

/**
 * rsa復号化 JSEncrypt
 * @param options rsaの秘密鍵keyと暗号文content
 * @returns 平文
 */
function rsaDecrypt(options) {
  const { key, content } = options;
  const decrypt = new JSEncrypt();
  decrypt.setPrivateKey(key);
  return decrypt.decrypt(content) || "";
}

AES 使用 crypto#

バックエンドが AES で暗号化し、フロントエンドが AES で復号化する際に問題が発生する可能性が高く、文字と形式の問題が原因です。バックエンドに解決を依頼することをお勧めします。

// AES 暗号化関数
export const aesEncrypt = (data, key) => {
  const cipher = createCipheriv("aes-128-ecb", Buffer.from(key), null);
  let encrypted = cipher.update(data, "utf8", "hex");
  encrypted += cipher.final("hex");
  return encrypted;
};

// AES 復号化関数
export const aesDecrypt = (encryptedData, key) => {
  const decipher = createDecipheriv("aes-128-ecb", Buffer.from(key), null);
  let decrypted = decipher.update(encryptedData, "hex", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
};

SM4 使用 sm-crypto#

import { sm4 } from "sm-crypto";
/**
 * sm4復号化
 * @param options sm4の秘密鍵keyと暗号文content
 * @param cipherMode  CMACモード 1 - C1C3C2,0 - C1C2C3,デフォルトは1
 * @returns 暗号文
 */
function sm4Decrypt(options: DecryptOptions, cipherMode = 1): string {
  const { key, content } = options;
  return (
    sm4.decrypt(_base64ToHex(content), _base64ToHex(key), cipherMode) || ""
  );
}
// 暗号化
sm4.encrypt(inArray, key, options);

エンコーディング変換#

// 16進数文字列をバイト配列に変換
export function hexToBase64(hex) {
  const byteArray = new Uint8Array(
    hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
  );

  // バイト配列をBase64文字列に変換
  let binaryString = "";
  const len = byteArray.length;
  for (let i = 0; i < len; i++) {
    binaryString += String.fromCharCode(byteArray[i]);
  }

  return btoa(binaryString);
}
// Base64文字列をバイト配列に変換
export function base64ToHex(base64) {
  const binaryString = atob(base64);
  const binaryLen = binaryString.length;
  let hexString = "";

  for (let i = 0; i < binaryLen; i++) {
    const hex = binaryString.charCodeAt(i).toString(16);
    hexString += (hex.length === 1 ? "0" : "") + hex; // 各バイトが2桁になるようにする
  }

  return hexString;
}

フックをラッピングしてフロントエンドの多重復号化を完了する#

まず RSA で SM4 の秘密鍵を復号化し、その後 SM4 で暗号文を復号化します。

import JSEncrypt from "jsencrypt";
import { sm4 } from "sm-crypto";

/**
 * データ復号化プロセス:
 * まずRSAでSM4の秘密鍵を復号化し、その後SM4で暗号文を復号化します。
 * @param options IJSEncryptOptions RSA秘密鍵、SM4暗号鍵、暗号文
 * @returns DecryptResult 最終的な平文、RSA復号化関数、SM4復号化関数
 */
export function useEncryption(options) {
  const { sm4EnCryptBaseKey, contentEncryptBase64, rsaPrivateKey } = options;
  /**
   * データ復号化
   * @returns 平文
   */
  function _decode() {
    const sm4key = rsaDecrypt({
      key: rsaPrivateKey,
      content: sm4EnCryptBaseKey,
    });
    return sm4Decrypt({ key: sm4key || "", content: contentEncryptBase64 });
  }

  /**
   * rsa復号化
   * @param options rsaの秘密鍵keyと暗号文content
   * @returns 平文
   */
  function rsaDecrypt(options) {
    const { key, content } = options;
    const decrypt = new JSEncrypt();
    decrypt.setPrivateKey(key);
    return decrypt.decrypt(content) || "";
  }

  /**
   * sm4復号化
   * @param options sm4の秘密鍵keyと暗号文content
   * @param cipherMode  CMACモード 1 - C1C3C2,0 - C1C2C3,デフォルトは1
   * @returns 平文
   */
  function sm4Decrypt(options, cipherMode = 1) {
    const { key, content } = options;
    return (
      sm4.decrypt(_base64ToHex(content), _base64ToHex(key), cipherMode) || ""
    );
  }

  /**
   * 16進数をbase64に変換
   */
  function _hexToBase64(hex) {
    const byteArray = new Uint8Array(
      hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
    );
    let binaryString = "";
    const len = byteArray.length;
    for (let i = 0; i < len; i++) {
      binaryString += String.fromCharCode(byteArray[i]);
    }
    return btoa(binaryString);
  }

  /**
   * base64を16進数に変換
   */
  function _base64ToHex(base64) {
    const binaryString = atob(base64);
    const binaryLen = binaryString.length;
    let hexString = "";
    for (let i = 0; i < binaryLen; i++) {
      const hex = binaryString.charCodeAt(i).toString(16);
      hexString += (hex.length === 1 ? "0" : "") + hex;
    }
    return hexString;
  }

  return { content: _decode(), rsaDecrypt, sm4Decrypt };
}

この記事は Mix Space によって xLog に同期更新されました。原始リンクは http://www.sroxck.top/posts/fontend/crypt

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。