The company's latest requirement surprisingly has the frontend using a key to decrypt... It's really a case of guarding against gentlemen but not against rogues. Here, I will record the encryption and decryption process using RSA, AES, SM4, etc., as well as the encapsulation of hooks.
Private Key and Public Key#
- Public Key:
Can be shared with anyone and is used to encrypt data. Data encrypted with the public key can only be decrypted with the corresponding private key. - Private Key:
Must be kept secret and is held only by the key owner. Data decrypted with the private key can only be decrypted if it was encrypted with the corresponding public key.
Workflow#
- Generate Key Pair: Generate a pair of keys, including a public key and a private key.
- Data Encryption: The sender uses the receiver's public key to encrypt data.
- Data Transmission: The encrypted data can be safely sent through an insecure channel.
- Data Decryption: The receiver uses their private key to decrypt the received data.
Using Frontend Encryption Libraries#
RSA Using jsencrypt or node-rsa#
import JSEncrypt from "jsencrypt";
// Generate RSA key pair
export const generateKeyPair = () => {
const key = new NodeRSA({ b: 512 });
return {
publicKey: key.exportKey("public"),
privateKey: key.exportKey("private"),
};
};
// RSA encryption function
export const rsaEncrypt = (data, publicKey) => {
const key = new NodeRSA(publicKey);
return key.encrypt(data, "base64");
};
// RSA decryption function
export const rsaDecrypt = (encryptedData, privateKey) => {
const key = new NodeRSA(privateKey);
return key.decrypt(encryptedData, "utf8");
};
/**
* RSA decryption using JSEncrypt
* @param options RSA private key and encrypted content
* @returns Plain text
*/
function rsaDecrypt(options) {
const { key, content } = options;
const decrypt = new JSEncrypt();
decrypt.setPrivateKey(key);
return decrypt.decrypt(content) || "";
}
AES Using crypto#
When the backend uses AES for encryption and the frontend uses AES for decryption, it is likely to cause issues that prevent the frontend from decrypting due to character and format problems. It is recommended to have the backend resolve this.
// AES encryption function
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 decryption function
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 Using sm-crypto#
import { sm4 } from "sm-crypto";
/**
* SM4 decryption
* @param options SM4 private key and encrypted content
* @param cipherMode CMAC mode 1 - C1C3C2, 0 - C1C2C3, default is 1
* @returns Encrypted content
*/
function sm4Decrypt(options: DecryptOptions, cipherMode = 1): string {
const { key, content } = options;
return (
sm4.decrypt(_base64ToHex(content), _base64ToHex(key), cipherMode) || ""
);
}
// Encryption
sm4.encrypt(inArray, key, options);
Encoding Conversion#
// Convert hexadecimal string to byte array
export function hexToBase64(hex) {
const byteArray = new Uint8Array(
hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
);
// Convert byte array to Base64 string
let binaryString = "";
const len = byteArray.length;
for (let i = 0; i < len; i++) {
binaryString += String.fromCharCode(byteArray[i]);
}
return btoa(binaryString);
}
// Convert Base64 string to byte array
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; // Ensure each byte is two digits
}
return hexString;
}
Encapsulating Hooks to Complete Frontend Multi-layer Decryption#
First decrypt the SM4 key using RSA, then decrypt the ciphertext using SM4.
import JSEncrypt from "jsencrypt";
import { sm4 } from "sm-crypto";
/**
* Data decryption process:
* First decrypt the SM4 key using RSA, then decrypt the ciphertext using SM4.
* @param options IJSEncryptOptions RSA key, SM4 encryption key, ciphertext
* @returns DecryptResult Final plaintext, RSA decryption function, SM4 decryption function
*/
export function useEncryption(options) {
const { sm4EnCryptBaseKey, contentEncryptBase64, rsaPrivateKey } = options;
/**
* Data decryption
* @returns Plain text
*/
function _decode() {
const sm4key = rsaDecrypt({
key: rsaPrivateKey,
content: sm4EnCryptBaseKey,
});
return sm4Decrypt({ key: sm4key || "", content: contentEncryptBase64 });
}
/**
* RSA decryption
* @param options RSA private key and encrypted content
* @returns Plain text
*/
function rsaDecrypt(options) {
const { key, content } = options;
const decrypt = new JSEncrypt();
decrypt.setPrivateKey(key);
return decrypt.decrypt(content) || "";
}
/**
* SM4 decryption
* @param options SM4 private key and encrypted content
* @param cipherMode CMAC mode 1 - C1C3C2, 0 - C1C2C3, default is 1
* @returns Plain text
*/
function sm4Decrypt(options, cipherMode = 1) {
const { key, content } = options;
return (
sm4.decrypt(_base64ToHex(content), _base64ToHex(key), cipherMode) || ""
);
}
/**
* Convert hexadecimal to 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);
}
/**
* Convert base64 to hexadecimal
*/
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 };
}
This article is synchronized and updated to xLog by Mix Space. The original link is http://www.sroxck.top/posts/fontend/crypt