318 lines
10 KiB
JavaScript
Raw Permalink Normal View History

2026-07-27 11:28:33 +08:00
/**
* 功能加解密工具类第三方系统及对外接口专用版
* 说明使用国密SM4算法
* JavaScript版本实现
*/
const sm4 = require('sm-crypto').sm4;
const crypto = require('crypto');
// 密文前缀常量
const CIPHERTEXT_PREFIX_THIRD_AES128 = "|$|";
const CIPHERTEXT_PREFIX_THIRD_AES128_REGEX = /\|\$\|/;
const CIPHERTEXT_PREFIX_THIRD_SM4ECB = "|$4|";
const CIPHERTEXT_PREFIX_THIRD_SM4ECB_REGEX = /\|\$4\|/;
/**
* 判断字符串是否为空
* @param {string} str - 待判断的字符串
* @returns {boolean} 是否为空
*/
function isBlank(str) {
return !str || str.length === 0;
}
/**
* 将字节数组转换为Base64字符串
* @param {Buffer} binaryData - 字节数组
* @returns {string} Base64字符串
*/
function base64StringFromByteArray(binaryData) {
try {
const base64String = binaryData.toString('base64');
return base64String
} catch (e) {
console.error('Exception - base64StringFromByteArray() | binaryData :', binaryData);
return null;
}
}
/**
* 将Base64字符串转换为字节数组
* @param {string} base64String - Base64字符串
* @returns {Buffer} 字节数组
*/
function byteArrayFromBase64String(base64String) {
try {
return Buffer.from(base64String, 'base64');
} catch (e) {
console.error('Exception - byteArrayFromBase64String() | base64String :', base64String);
return null;
}
}
/**
* SM4 ECB模式加密带PKCS5Padding
* @param {Buffer} content - 待加密内容
* @param {Buffer} key - SM4密钥16字节
* @returns {Buffer} 加密后的字节数组
*/
function encryptECBPadding(content, key) {
try {
// sm-crypto 的 sm4.encrypt 使用 ECB 模式,自动处理 PKCS5Padding
// 密钥需要转换为十六进制字符串内容需要转换为UTF-8字符串
const keyHex = key.toString('hex');
const contentStr = content.toString('utf8');
// sm4.encrypt(data, key, mode, outputEncoding)
// mode: 1=ECB, 2=CBC; outputEncoding: 'base64' 或 'hex',默认为 'base64'
// 第一个参数应该是UTF-8字符串而不是hex字符串
const encryptedHex = sm4.encrypt(contentStr, keyHex, 1, 'hex');
return Buffer.from(encryptedHex, 'hex');
} catch (e) {
console.error('Exception-encryptECBPadding()', e.message);
return null;
}
}
/**
* SM4 ECB模式解密带PKCS5Padding
* @param {Buffer} cipherText - 待解密内容
* @param {Buffer} key - SM4密钥16字节
* @returns {Buffer} 解密后的字节数组
*/
function decryptECBPadding(cipherText, key) {
try {
// sm-crypto 的 sm4.decrypt 使用 ECB 模式,自动处理 PKCS5Padding
const keyHex = key.toString('hex');
const cipherTextHex = cipherText.toString('hex');
// sm4.decrypt(data, key, mode, outputEncoding)
// mode: 1=ECB, 2=CBC; outputEncoding: 'base64' 或 'hex',默认为 'base64'
// 第一个参数应该是hex字符串密文的hex表示
const decryptedHex = sm4.decrypt(cipherTextHex, keyHex, 1, 'hex');
return Buffer.from(decryptedHex, 'hex');
} catch (e) {
console.error('Exception-decryptECBPadding()', e.message);
return null;
}
}
/**
* SM4 ECB模式加密Base64输入输出
* @param {string} content - 待加密内容
* @param {string} key - Base64编码的SM4密钥
* @returns {string} Base64编码的加密结果
*/
function encryptECBPaddingBase64(content, key) {
if (isBlank(content) || isBlank(key)) {
console.error('【警告】encryptECBPaddingBase64():[IN]content or [IN]key is null!|content=' + content);
return content;
}
try {
const contentBuffer = Buffer.from(content, 'utf8');
const keyBuffer = byteArrayFromBase64String(key);
if (!keyBuffer) {
return null;
}
const encryptedBuffer = encryptECBPadding(contentBuffer, keyBuffer);
if (!encryptedBuffer) {
return null;
}
return base64StringFromByteArray(encryptedBuffer);
} catch (e) {
console.error('Exception-encryptECBPaddingBase64()', e.message);
return null;
}
}
/**
* SM4 ECB模式解密Base64输入输出
* @param {string} cipherText - Base64编码的密文
* @param {string} key - Base64编码的SM4密钥
* @returns {string} 解密后的明文
*/
function decryptECBPaddingBase64(cipherText, key) {
if (isBlank(cipherText) || isBlank(key)) {
console.error('【警告】decryptECBPaddingBase64():[IN]cipherText or [IN]key is null!|cipherText=' + cipherText);
return cipherText;
}
try {
const cipherTextBuffer = byteArrayFromBase64String(cipherText);
const keyBuffer = byteArrayFromBase64String(key);
if (!cipherTextBuffer || !keyBuffer) {
return null;
}
const decryptedBuffer = decryptECBPadding(cipherTextBuffer, keyBuffer);
if (!decryptedBuffer) {
return null;
}
return decryptedBuffer.toString('utf8');
} catch (e) {
console.error('Exception-decryptECBPaddingBase64()', e.message);
return null;
}
}
/**
* AES128加密
* @param {string} content - 待加密内容
* @param {Buffer} byteAESKey - AES密钥16字节
* @returns {Buffer} 加密后的字节数组
*/
function encryptAES(content, byteAESKey) {
try {
const cipher = crypto.createCipheriv('aes-128-ecb', byteAESKey, null);
cipher.setAutoPadding(true);
let encrypted = cipher.update(content, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
return encrypted;
} catch (e) {
console.error('Exception - encryptAES()', e.message);
return null;
}
}
/**
* AES128解密
* @param {Buffer} content - 待解密内容
* @param {Buffer} byteAESKey - AES密钥16字节
* @returns {Buffer} 解密后的字节数组
*/
function decryptAES(content, byteAESKey) {
try {
const decipher = crypto.createDecipheriv('aes-128-ecb', byteAESKey, null);
decipher.setAutoPadding(true);
let decrypted = decipher.update(content);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted;
} catch (e) {
console.error('Exception - decryptAES()', e.message);
return null;
}
}
/**
* AES128加密Base64输入输出
* @param {string} content - 待加密内容
* @param {string} base64AESKey - Base64编码的AES密钥
* @returns {string} Base64编码的加密结果
*/
function encryptBase64(content, base64AESKey) {
try {
const keyBuffer = byteArrayFromBase64String(base64AESKey);
if (!keyBuffer) {
return null;
}
const encryptedBuffer = encryptAES(content, keyBuffer);
if (!encryptedBuffer) {
return null;
}
return base64StringFromByteArray(encryptedBuffer);
} catch (e) {
console.error('Exception - encryptBase64()', e.message);
return null;
}
}
/**
* AES128解密Base64输入输出
* @param {string} base64Content - Base64编码的密文
* @param {Buffer} byteAESKey - AES密钥16字节
* @returns {Buffer} 解密后的字节数组
*/
function decryptBase64FromBuffer(base64Content, byteAESKey) {
try {
const contentBuffer = byteArrayFromBase64String(base64Content);
if (!contentBuffer) {
return null;
}
return decryptAES(contentBuffer, byteAESKey);
} catch (e) {
console.error('Exception - decryptBase64FromBuffer()', e.message);
return null;
}
}
/**
* AES128解密Base64输入输出返回字符串
* @param {string} base64Content - Base64编码的密文
* @param {string} base64AESKey - Base64编码的AES密钥
* @returns {string} 解密后的明文
*/
function decryptBase64(base64Content, base64AESKey) {
try {
const keyBuffer = byteArrayFromBase64String(base64AESKey);
if (!keyBuffer) {
return null;
}
const decryptedBuffer = decryptBase64FromBuffer(base64Content, keyBuffer);
if (!decryptedBuffer) {
return null;
}
return decryptedBuffer.toString('utf8');
} catch (e) {
console.error('Exception - decryptBase64()', e.message);
return null;
}
}
/**
* 功能国密商用SM4加密
* 说明用于加密其它敏感类数据带密钥参数
* @param {string} plaintext - 明文
* @param {string} key - Base64编码的SM4密钥
* @returns {string} 加密后的密文带前缀 |$4|
*/
function sm4Encrypt(plaintext, key) {
if (isBlank(plaintext) || isBlank(key)
|| plaintext.startsWith(CIPHERTEXT_PREFIX_THIRD_AES128)
|| plaintext.startsWith(CIPHERTEXT_PREFIX_THIRD_SM4ECB)
) {
return plaintext;
} else {
const tmp = encryptECBPaddingBase64(plaintext, key);
if (tmp == null) {
console.error('ERROR-sm4Encrypt():plaintext=' + plaintext + ',encryptRes=' + tmp);
}
return tmp != null ? (CIPHERTEXT_PREFIX_THIRD_SM4ECB + tmp) : null;
}
}
/**
* 功能国密商用SM4解密
* 说明用于解密其它敏感类数据带密钥参数兼容AES128算法
* @param {string} ciphertext - 密文
* @param {string} key - Base64编码的SM4密钥
* @returns {string} 解密后的明文
*/
function sm4Decrypt(ciphertext, key) {
if (isBlank(ciphertext) || isBlank(key)) {
return ciphertext;
}
if (ciphertext.startsWith(CIPHERTEXT_PREFIX_THIRD_SM4ECB)) {
const tmp = decryptECBPaddingBase64(ciphertext.replace(CIPHERTEXT_PREFIX_THIRD_SM4ECB_REGEX, ''), key);
if (tmp == null) {
console.error('ERROR-sm4Decrypt():ciphertext=' + ciphertext + ',decryptRes=' + tmp);
}
return tmp;
} else if (ciphertext.startsWith(CIPHERTEXT_PREFIX_THIRD_AES128)) {
const tmp = decryptBase64(ciphertext.replace(CIPHERTEXT_PREFIX_THIRD_AES128_REGEX, ''), key);
if (tmp == null) {
console.error('ERROR-sm4Decrypt():ciphertext=' + ciphertext + ',decryptRes=' + tmp);
}
return tmp;
} else {
return ciphertext;
}
}
// 导出函数
module.exports = {
sm4Encrypt,
sm4Decrypt,
encryptECBPaddingBase64,
decryptECBPaddingBase64,
encryptBase64,
decryptBase64
};