import { SM4 } from "../../utils/sm4"
/**
* SM4 分组密码算法
*
* @description SM4 是中华人民共和国密码行业标准,由国家密码管理局于 2012 年 3 月发布。
* SM4 算法是一种对称分组密码算法,分组长度为 128 位,密钥长度为 128 位。
* 该算法采用非线性变换、线性变换和轮密钥加的迭代结构,
* 加密轮数为 32 轮,具有高安全性和高效率的特点。
*
* @module SM4
* @category sm
* @alias sm4
* @author xkloveme <xkloveme@gmail.com>
* @since 0.1.0
* @date 2024-08-10 21:53:59
*
* @see {@link https://www.oscca.gov.cn/sca/xxgk/2012-03/21/content_1002386.shtml} SM4 分组密码算法标准
* @see {@link sm2} SM2 椭圆曲线公钥密码算法
* @see {@link sm3} SM3 密码杂凑算法
*
* @param {string|Uint8Array} originalData - 待加密的数据
* @param {string|Uint8Array} key - 加密密钥(128 位/16 字节)
* @param {Object} [options] - 加密选项
* @param {('ecb'|'cbc')} [options.mode='ecb'] - 加密模式
* @param {('pkcs7'|'none')} [options.padding='pkcs7'] - 填充方式
* @param {('hex'|'array')} [options.output='hex'] - 输出格式
* @param {string|Uint8Array} [options.iv] - 初始向量(CBC 模式下必需)
*
* @returns {string|Uint8Array} 加密后的数据
*
* @example
* // 基本加密操作(ECB 模式)
* import { sm4 } from 'jxk'
*
* const plaintext = '机密数据内容'
* const key = '0123456789abcdeffedcba9876543210' // 128 位密钥(32 个十六进制字符)
*
* // 默认 ECB 模式加密
* const encrypted = sm4.encrypt(plaintext, key)
* console.log('密文:', encrypted)
*
* // 解密
* const decrypted = sm4.decrypt(encrypted, key)
* console.log('明文:', decrypted) // '机密数据内容'
*
* @example
* // CBC 模式加密(更安全)
* import { sm4 } from 'jxk'
*
* const plaintext = '机密数据内容'
* const key = '0123456789abcdeffedcba9876543210'
* const iv = 'fedcba98765432100123456789abcdef' // 128 位初始向量
*
* // CBC 模式加密
* const encrypted = sm4.encrypt(plaintext, key, {
* mode: 'cbc',
* iv: iv
* })
*
* // CBC 模式解密
* const decrypted = sm4.decrypt(encrypted, key, {
* mode: 'cbc',
* iv: iv
* })
*
* @example
* // 不同输出格式示例
* const data = '测试数据'
* const key = '0123456789abcdeffedcba9876543210'
*
* // 十六进制输出(默认)
* const hexResult = sm4.encrypt(data, key, { output: 'hex' })
* console.log('十六进制:', hexResult)
*
* // 字节数组输出
* const arrayResult = sm4.encrypt(data, key, { output: 'array' })
* console.log('字节数组:', arrayResult)
*
* @example
* // 无填充模式(数据长度必须是 16 的倍数)
* const data = '1234567890123456' // 16 字节数据
* const key = '0123456789abcdeffedcba9876543210'
*
* const encrypted = sm4.encrypt(data, key, { padding: 'none' })
* const decrypted = sm4.decrypt(encrypted, key, { padding: 'none' })
*
* @example
* // 批量数据加密(大文件处理)
* const largeData = 'x'.repeat(10000) // 10KB 数据
* const key = '0123456789abcdeffedcba9876543210'
*
* console.time('SM4 加密耗时')
* const encrypted = sm4.encrypt(largeData, key)
* console.timeEnd('SM4 加密耗时')
*
* console.time('SM4 解密耗时')
* const decrypted = sm4.decrypt(encrypted, key)
* console.timeEnd('SM4 解密耗时')
*
* @example
* // 与 SM2 结合使用(混合加密)
* import { sm2, sm4 } from 'jxk'
*
* // 1. 生成 SM2 密钥对
* const keyPair = sm2.generateKeyPairHex()
*
* // 2. 生成 SM4 对称密钥
* const sm4Key = '0123456789abcdeffedcba9876543210'
*
* // 3. 使用 SM4 加密大量数据
* const largeData = '大量机密数据...'
* const encryptedData = sm4.encrypt(largeData, sm4Key)
*
* // 4. 使用 SM2 加密 SM4 密钥
* const encryptedKey = sm2.doEncrypt(sm4Key, keyPair.publicKey)
*
* // 5. 传输 { encryptedData, encryptedKey }
*
* // 6. 解密过程:
* // 首先使用 SM2 解密出 SM4 密钥
* const decryptedSm4Key = sm2.doDecrypt(encryptedKey, keyPair.privateKey)
* // 然后使用 SM4 密钥解密数据
* const decryptedData = sm4.decrypt(encryptedData, decryptedSm4Key)
*
* @security
* 安全注意事项:
* 1. 密钥必须是 128 位(16 字节)长度,使用高质量随机数生成
* 2. 生产环境中建议使用 CBC 模式而非 ECB 模式
* 3. CBC 模式下的初始向量 (IV) 必须唯一且不可预测
* 4. 密钥存储和传输必须加密保护,不得明文存储
* 5. 定期轮换密钥,避免长期使用同一密钥
* 6. 对于大文件加密,考虑使用流式加密方式
* 7. 加密后的数据应进行完整性校验(如使用 SM3 哈希)
*
* @example
* 加密
* import {sm4} from "jxk"
* const msg = '我是原始数据' // 可以为 utf8 串或字节数组
* const key = '5e0a3ab263b283e3db6001018776c4f0' // 可以为 16 进制串或字节数组,要求为 128 比特 *
* let encryptData = sm4.encrypt(msg, key) // 加密,默认输出 16 进制字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 充)
* let encryptData = sm4.encrypt(msg, key, {padding: 'none'}) // 加密,不使用 padding
* let encryptData = sm4.encrypt(msg, key, {padding: 'none', output: 'array'}) // 加密,不使用 padding,输出为字节数组
* let encryptData = sm4.encrypt(msg, key, {mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'}) // 加密,cbc 模式
* @example
* 解密
import {sm4} from "jxk"
const encryptData = 'aaff18e2a966d10017469a492b800169d68e6f979da91cdeed454bb769665892' // 可以为 16 进制串或字节数组
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特
let decryptData = sm4.decrypt(encryptData, key) // 解密,默认输出 utf8 字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
let decryptData = sm4.decrypt(encryptData, key, {padding: 'none'}) // 解密,不使用 padding
let decryptData = sm4.decrypt(encryptData, key, {padding: 'none', output: 'array'}) // 解密,不使用 padding,输出为字节数组
let decryptData = sm4.decrypt(encryptData, key, {mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'}) // 解密,cbc 模式
* @author xkloveme <xkloveme@gmail.com>
* @Date: 2024-08-10 21:53:59
*/
export default {
/**
* 加密数据
* @param {string|Uint8Array} originalData - 待加密的数据
* @param {string|Uint8Array} key - 加密密钥
* @param {Object} [options] - 加密选项
* @param {('ecb'|'cbc')} [options.mode='ecb'] - 加密模式
* @param {('pkcs7'|'none')} [options.padding='pkcs7'] - 填充方式
* @param {('hex'|'array')} [options.output='hex'] - 输出格式
* @param {string|Uint8Array} [options.iv] - 初始向量
* @returns {string|Uint8Array} - 加密后的数据
*/
encrypt: (originalData, key, options = {}) => {
if (originalData === '' || originalData === null || originalData === undefined) {
return originalData;
}
try {
const encrypted = SM4(originalData + '', key, 1, options);
if (options.output === 'array') {
return encrypted;
} else {
return encrypted.toString('hex');
}
} catch (error) {
console.error('🐛: ~ encrypt ~ error:', originalData, error);
return originalData;
}
},
/**
* 解密数据
* @param {string|Uint8Array} encryptedData - 待解密的数据
* @param {string|Uint8Array} key - 解密密钥
* @param {Object} [options] - 解密选项
* @param {('ecb'|'cbc')} [options.mode='ecb'] - 解密模式
* @param {('pkcs7'|'none')} [options.padding='pkcs7'] - 填充方式
* @param {('utf8'|'array')} [options.output='utf8'] - 输出格式
* @param {string|Uint8Array} [options.iv] - 初始向量
* @returns {string|Uint8Array} - 解密后的数据
*/
decrypt: (encryptedData, key, options = {}) => {
if (encryptedData === '' || encryptedData === null || encryptedData === undefined) {
return encryptedData;
}
try {
const decrypted = SM4(encryptedData, key, 0, options);
if (decrypted === '') {
throw new Error('Decryption failed')
}
if (options.output === 'array') {
return decrypted;
} else {
const decryptedStr = decrypted.toString('utf8');
try {
// 尝试解析为 JSON 对象
if(decryptedStr.includes('}') || decryptedStr.includes(']')){
return JSON.parse(decryptedStr);
}else{
return decryptedStr;
}
} catch (parseError) {
// 如果解析失败,返回原始字符串
return decryptedStr;
}
}
} catch (error) {
console.error('🐛: ~ decrypt ~ error:', encryptedData, error);
return encryptedData;
}
}
}